1.线程与多线程的概念:在一个程序中,能够独立运行的程序片段叫作“线程”(Thread)。多线程(multithreading)是指从软件或者硬件上实现多个线程并发执行的技术。

2.多线程的意义:多线程可以在时间片里被cpu快速切换,资源能更好被调用、程序设计在某些情况下更简单、程序响应更快、运行更加流畅。

2.如何启动一个线程:继承Thread类、实现Runnable接口、实现Callable接口

3.为什么要保证线程的同步?:java允许多线程并发控制,当多个线程同时操作一个可共享的资源变量时(如数据的增删改查),将会导致数据不准确,相互之间产生冲突,因此加入同步锁以避免在该线程没有完成操作之前,被其他线程的调用,从而保证了该变量的唯一性和准确性。

4.基本的线程同步:使用synchronized关键字、特殊域变量volatile、wait和notify方法等。

public class T {
private int count=10;
private Object o=new Object(); public void m(){
synchronized (o){
//任何线程要执行下面的代码,必须先拿到o的锁
count--;
System.out.println(Thread.currentThread().getName()+"count="+count);
}
}
}

假设这段代码中有多个线程,当第一个线程执行到m方法来的时候,sync锁住了堆内存中的o对象,这个时候第二个线程是进不来的,它必须等第一个线程执行完,锁释放掉才可以接着执行。这里有个锁的概念叫做互斥锁,sync是一种互斥锁。

public class T1 {
private static int count =10; public synchronized static void m(){
//这里等同于synchronized(T3.class)
count--;
System.out.println(Thread.currentThread().getName()+"count="+count);
} public static void mm(){
synchronized (T1.class){
//思考:这里写成synchronized(this)是否可以?
count--;
}
}
}

思考这里,注意sync修饰的是一个静态方法和静态的属性,静态修饰的方法和属性是不需要new出对象来就可以访问的,所以这里没有new出对象,sync锁定的是T3.class对象。

public class T2 implements Runnable{
private int count =10; @Override
public /*synchronized*/ void run(){
count--;
System.out.println(Thread.currentThread().getName()+"count="+count);
} public static void main(String[] args) {
T2 t=new T2();
for (int i=0;i<5;i++){
new Thread(t,"THREAD"+i).start();
}
}
}

上面代码中开启了五个线程,执行run方法对count进行减一的操作,这里会出现线程抢占资源的问题。当第一个线程在执行run方法时,减减的过程中,第二个线程也进入了方法,同样也在执行减减,可能会出现第二个线程减完的时候,第一个线程才输出count,这时候就出现了线程重入。处理方法可以加上synchronized关键字,只有等第一个线程执行完毕,第二个线程才可以进入,即同一时刻只能有一个线程来对它进行操作,也就是原子性。

public class Account {

    /**
* 账号持有人和账号余额
*/
String name;
double balance; /**
* 设置账号余额,线程睡眠目的是先让它读数据
* @param name
* @param balance
*/
public synchronized void set(String name,double balance){
this.name=name; try {
Thread.sleep(2000);
}catch (InterruptedException e){
e.printStackTrace();
}
this.balance=balance;
} public double getBalance(String name){
return this.balance;
} public static void main(String[] args) {
Account a=new Account();
new Thread(()->a.set("zhangsan",100.0)).start(); try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(a.getBalance("zhangsan")); try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(a.getBalance("zhangsan"));
}
}

这是一个小demo,代码中只对set方法加了锁,没有对get方法加锁,这个时候会出现脏读现象。解决方法是读和写的方法都加锁。

public class T3 {

    synchronized void m1(){
System.out.println("m1 start");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
m2();
} synchronized void m2() {
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("m2");
}
}

这个案例中,m1和m2方法都已经加上了锁,当执行m1的时候再去执行m2,这样是可以的。一个同步方法可以调用另外一个同步方法,也就是说sync获得的锁是可重入锁。还有个概念是死锁,死锁是指多个线程抢占资源而造成的一种互相等待。举个例子,一个箱子需要两把钥匙才可以打开,钥匙分别在两个人手中,这两个人互相抢占另外一个人的钥匙,导致箱子打不开。

Java并发编程(一):线程基础知识以及synchronized关键字的更多相关文章

  1. Java并发编程笔记—摘抄—基础知识

    什么是线程安全 当多个线程访问某个类时,不管运行环境采用何种调度方式或者这些线程如何交替执行,并且在主调代码中不需要任何额外的同步或协同,这个类都能表现出正确的行为,那么就称这个类是线程安全的. 竞态 ...

  2. Java并发编程:线程控制

    在上一篇文章中(Java并发编程:线程的基本状态)我们介绍了线程状态的 5 种基本状态以及线程的声明周期.这篇文章将深入讲解Java如何对线程进行状态控制,比如:如何将一个线程从一个状态转到另一个状态 ...

  3. Java并发编程:线程池的使用

    Java并发编程:线程池的使用 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了, ...

  4. Java并发编程:线程间协作的两种方式:wait、notify、notifyAll和Condition

    Java并发编程:线程间协作的两种方式:wait.notify.notifyAll和Condition 在前面我们将了很多关于同步的问题,然而在现实中,需要线程之间的协作.比如说最经典的生产者-消费者 ...

  5. Java并发编程:线程池的使用(转)

    Java并发编程:线程池的使用 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了, ...

  6. Java 并发编程:线程间的协作(wait/notify/sleep/yield/join)

    Java并发编程系列: Java 并发编程:核心理论 Java并发编程:Synchronized及其实现原理 Java并发编程:Synchronized底层优化(轻量级锁.偏向锁) Java 并发编程 ...

  7. (转)Java并发编程:线程池的使用

    背景:线程池在面试时候经常遇到,反复出现的问题就是理解不深入,不能做到游刃有余.所以这篇博客是要深入总结线程池的使用. ThreadPoolExecutor的继承关系 线程池的原理 1.线程池状态(4 ...

  8. Java并发编程:线程池的使用(转载)

    转载自:https://www.cnblogs.com/dolphin0520/p/3932921.html Java并发编程:线程池的使用 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实 ...

  9. Java并发编程:线程池的使用(转载)

    文章出处:http://www.cnblogs.com/dolphin0520/p/3932921.html Java并发编程:线程池的使用 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实 ...

随机推荐

  1. 「CF1082C」Multi-Subject Competition 解题报告

    人生第一篇题解,虽然这道题做的人暂时不多,但我相信它--迟早有一天会发扬光大的!!! 说完废话 步入正题 题意: 传送门 思路: 模拟.枚举 对于每个组里的数字,先排序,然后从一到最大可能的情况,枚举 ...

  2. Vue+Vant+Vuex实现本地购物车功能

    通常,我们做移动端商城的时候,通常会有购物车模块,那购物车模块有两种实现方式,一是储存在后台通过接口获取到购物车信息,二是存在用户手机本地,第一种方法只要调用接口获取比较简单,这里介绍的是第二种方法, ...

  3. zookeeper作配置中心(存储支付信息)

    zookeeper作配置中心(存储敏感信息) 前提:最近在项目中需要用到支付接口,支付宝或者微信支付,根据官方文档,需要配置一些诸如notify-url或者app-private-key等信息,这些信 ...

  4. 2019牛客暑期多校第五场题解ABGH

    A.digits 2 传送门 题意:给你一个n,要求输出一个每一位数字之和能整除n且其本身也能整除n的数.n不超过100,要求的数不超过10000位数. 题解:直接将n输出n次. 代码: #inclu ...

  5. 比特币学习笔记(一)---在windows下编译搭建比特币环境

    最近打算研究下比特币源码,却发现这套源码正常情况下得在linux下编译运行,而我的机器是windows的. 怎么办呢? 起初打算用mingw和cygwin搞搞看,试了许久后发现行不通,必须转到linu ...

  6. 【转】Java面试题:多继承

    招聘和面试对开发经理来说是一个无尽头的工作,虽然有时你可以从HR这边获得一些帮助,但是最后还是得由你来拍板,或者就像另一篇文章“Java 面试题:写一个字符串的反转”所说: 面试开发人员不仅辛苦而且乏 ...

  7. 低功耗蓝牙(BLE)——概念

    1. 种类 单模蓝牙:仅支持传统蓝牙和BLE(低功耗蓝牙)中的一种: 双模蓝牙:同时支持传统蓝牙和BLE(低功耗蓝牙). 2. 部署方案 3. 节点类型 根据蓝牙协议不同的协议层有不同的角色 1. S ...

  8. js提取JSON数据中需要的那部分数据

    var data =[ { name: "程咬金",sex:"1",age:26 }, { name: "程才",sex:"0&q ...

  9. 1z0-062 题库解析3

    The hr user executes the following query on the employees table but does not issue commit, rollback, ...

  10. await Task.Yield(); 超简单理解!

    上面的代码类似于: Task.Run(() => { }).ContinueWith(t => Do(LoadData())); 意思就是: loadData 如果耗时较长那么上述代码会产 ...