赞
踩
前序:针对Day 5结尾的count++
多线程的执行,是随机调度抢占式的执行模式,某个线程执行指令过程中,当它执行到任何一个指令的时候,都有可能被其他线程把它的CPU抢占走
实际并发执行,由于上述原因以及count++本质是CPU的三个指令,两个线程执行指令的相对顺序就可能会存在多种可能,不同的执行顺序,得到的结果就可能会存在差异
(1)线程在系统中是随即调度的,抢占式执行的,这是线程不安全的罪魁祸首,万恶之源
(2)当前代码中,多个线程同时修改同一个变量
(3)线程针对变量的修改操作,不是“原子”的,count++这种操作不是原子的,是包含了三个指令
(4)内存可见性问题(后续介绍)
(5)指令重排序(后续介绍)
针对上述原因进行问题解决
原因(1)无法干预,属于内核设计,无法改变
原因(2)是一个切入点,但是在Java中,并不普适,针对特定场景可以使用,例如String是不可变对象
String为不可变对象:很好的保证线程安全;有稳定的哈希值;方便在常量池中缓存
原因(3)是解决线程安全问题最普适的方案,可以通过一些操作,把“非原子”操作,打包成一个“原子”操作,例如:加锁
如果某个代码操作,对应到一个CPU指令,就是原子的,对应到多个就不是原子的,每个代码最终变成哪些指令,需要对芯片手册(CPU指令集)要有比较深入的理解
锁:本质上是操作系统提供的功能,内核提供的功能,同过api给应用程序了,Java(JVM)对于这样的系统api又进行了封装(其他的语言,同样也可以封装/调用这样的系统api来完成加锁操作)
锁的操作主要是两个方面
锁的主要特性:互斥,一个线程获取到锁之后,另一个线程也尝试加这个锁,就会阻塞等待,也叫做锁竞争/锁冲突
代码中可以创建多个锁,只有多个线程竞争同一把锁,才会产生互斥,针对不同的锁,则不会
synchronized (locker){
.......
}
synchronized (locker)
,()里面就是写的“锁对象”
{}
中进入到代码块,就是给上述()锁对象进行了加锁操作,当出了代码块,就是给上述()锁对象进行了解锁操作package thread; public class Demo20 { private static int count = 0; public static void main(String[] args) throws InterruptedException { Object locker = new Object(); Thread t1 = new Thread(() ->{ for (int i = 0; i < 50000; i++) { synchronized (locker){ count++; } } }); Thread t2 = new Thread(() ->{ for (int i = 0; i < 50000; i++) { synchronized (locker){ count++; } } }); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println("count = " + count); } }
这两个线程中,每次进行count++是存在锁竞争的,会变成串行执行,但是执行for循环中的条件以及i++,仍然是并发执行的
package thread; class Counter { private int count = 0; //synchronized修饰普通方法,就相当于针对this加锁了 public void add() { synchronized (this){ count++; } } //上述方法也可以写成如下形式 synchronized public void add() { count++; } public int get(){ return count; } //synchronized修饰static方法,相当于针对该类的类对象加锁 public static void func() { synchronized(Counter.class){ //..... } } //上述方法也可以写成如下形式 synchronized public static void func(){ //...... } } public class Demo20 { public static void main(String[] args) throws InterruptedException { Counter counter = new Counter(); Thread t1 = new Thread(() ->{ for (int i = 0; i < 50000; i++) { counter.add(); //counter.func(); } }); Thread t2 = new Thread(() ->{ for (int i = 0; i < 50000; i++) { counter.add(); //counter.func(); } }); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println("count = " + counter.get()); } }
synchronized(Counter.class)
中的Counter.class
是反射,即程序运行时,能够拿到类一些属性信息,包括不限于
上述信息,最初都是程序员自己写的.java源代码中提供的
类名.class
来拿到这个类对象,一个java进程中,某个类,只能有唯一一个类对象所以,一旦多个线程调用func,则这些线程都会触发锁竞争
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。