volatile和synchronized的区别
在讲这个之前需要先了解下JMM(Java memory Model :java内存模型):并发过程中如何处理可见性、原子性、有序性的问题--建立JMM模型
详情请看:
导致多线程开发比较困难的点有两点:
- 线程之间如何通信:
- 共享内存:通过读写内存的公共状态进行隐式传递
- 消息传递:通过wait()/notify()/notifyAll()等方式,通过发送消息进行显示传递
- 线程之间如何同步:
- 在共享内存的并发模型中,同步是显示做的;synchronized
- 在消息传递的并发模型中,由于消息的发送必须在消息接收之前,所以同步是隐式的。
说明:Java里面所有的实例域、静态域、数组元素都存储在堆内存中的,堆内存是在多线程之间共享的;局部变量、方法、定义参数或者异常处理不会在线程之间共享的。是否共享导致了内存可见性的问题。(多线程读写同一个文件,多读多写同步进行,会导致混乱) 从上图看线程A和线程B什么时候去同步和更新主内存的共享内存都是不确定的,不确定的因素导致线程安全和线程可见性问题。
这时候引入volatile。
- 对于申明了volatile的变量在写操作的时候,JVM向处理器发送一条lock前缀的指令,把这个变量所在缓存行的数据写回到系统内存(主内存);
- 在多处理器的情况下,保证各个处理器缓存一致性的特点,就会实现缓存一致性协议。这里类似分布式情况下的多个数据库数据一致性、原子性。
- 每个处理器都去主内存中去检测自己本地缓存中的数据是否过期了(地址修改了),若修改了标志数据失效,当处理器对数据修改的时候,会去主内存同步数据到本地内存。 volatile可以做到原子性、可见性。不能保证复合操作的原子性。优点:相对synchronized开销小。
而synchronized:可重入性、互斥性、可见性。基于JVM实现的 以上是简单的实例在javap指令下查看其java编译器生成的字节码(JVM层面操作,所以synchronized没有源码)。 monitorenter和monitorexit分别是进入监视器,退出监视器,类似加锁和解锁。需要进入锁才能执行后面的方法。