总结Java并发编程中涉及到的一些知识
volatile
从双重检查锁加volatile引发的思考
一般我写单例模式是这样的
1 | public class Singleton { |
今天发现别人是这样写的
1 | ... |
这就产生了一个问题,为什么用了synchronized
之后,还要用volatile
来修饰singleton呢?
还好知乎在这个问题上有讨论
- 防止指令的重排,使得出现先赋值,再初始化的情况,而其他线程在第一次判断
singleton == null
的时候,就直接返回了一个尚未初始化完成的singleton - 操作的可见性,让抢到锁的线程1在创建完singleton实例之后,要把singleton从线程1的工作内存更新回主存
上面就介绍了volatile的有序性和可见性两种,volatile还有个原子性,但volatile只能保证对单次读/写的原子性,原子性是指一个操作是不可中断的,要么全部执行成功要么全部执行失败。
1 | x = 10; //原子操作 将数值10赋值给x |
1 | public class VolatileTest { |
所以上面的代码输出的值不一定为2000,因为i++
操作不具有原子性
synchronized
这个就比volatile牛逼多了,synchronized可以修饰方法、代码块,并且也保证了原子性与可见性。就上面i++的demo中把方法addI
用synchronized修饰一下保证每次的值都是2000。
两者主要差别
volatile是告知jvm,这个变量在寄存器中的值是不确定的,当某个线程修改了变量的值后会发出信号通知其他CPU将该变量的缓存行置为无效状态,因此当其他CPU需要读取这个变量时, 发现自己缓存中缓存该变量的缓存行是无效的,那么它就会从内存重新读。
synchronized是只允许持有锁的线程对这个变量进行操作,当前线程讲值写回主存之后,其他线程才会去读取(这个应该是这样的。。),就不会出现先读取值,然后再被其他线程修改,出现不一致的情况了。
wait notify notifyAll
Object类的三大方法,这三个方法,都是Java语言提供的实现线程间阻塞和控制进程内调度的底层机制。
wait():Causes the current thread to wait until another thread invokes the notify() method or the notifyAll() method for this object.
notify():Wakes up a single thread that is waiting on this object’s monitor.
notifyAll():Wakes up all threads that are waiting on this object’s monitor.
主要有2点要注意:
- 持有所
- 使用while判断条件成立而不是if
透过一个简单的例子来学习一下这些的使用
1 | /** |