多线程与高并发之synchronized与volatile

多线程与高并发之synchronized与volatile

  • synchronized
  • volatile

synchronized

在JDK 1.6之前 使用synchronized时直接向系统申请锁,系统从用户态切换到内核态去申请锁,导致synchronized性能比较差;
在JDK 1.6之后,JAVA对synchronized添加了锁升级来优化申请锁的流程。

偏向锁
系统发现不存在线程竞争时,会使用偏向锁,实际上就是在对象头上标记当前线程的id。

自旋锁(也叫轻量级锁、无锁、乐观锁)
1)当发生线程竞争时,锁会从偏向锁升级为自旋锁
2)但如果一开始就产生了竞争,会直接升级为自旋锁
自旋锁的原理是 CAS(Compare And Swap) 操作,CAS是原子操作,自旋的线程会尝试着去获取值,如果获取到了,就会执行代码逻辑得到新值,再比较之前读到的值和当前的值是否一致,一致的话就把新值写到内存里面;不一致说明值被其他线程修改了,放弃当前修改操作,重新尝试着去获取值,再进行处理、比较、交换操作。

重量级锁
默认自旋锁自旋10次升级为重量级锁,向系统申请锁

锁的使用场景
偏向锁:不会产生竞争的情况下
自旋锁:适合线程数不多并且线程执行时间短
重量级锁:线程数量比多,线程执行时间很长

volatile

volatile保证了变量的线程可见性和禁止指令重排序。

线程可见性
线程A修改了变量,会把值直接写回到主存1,线程B马上就能读到修改后的值,实现线程可见

禁止指令重排序和 DCL2 单例

问:DCL单例,是否需要加volatile?
答:要

CPU在执行编译后的代码时,为了提升执行效率,在不改变执行结果的前提下,会对代码的顺序进行改变。new 一个对象的步骤大概有3步:
1.申请内存,初始化变量,比如定义了 int a = 6; 这时候a的值是默认值0
2.变量赋值,比如定义了 int a = 6; 这时候才会真正的把6赋值给a
3.实例赋值
使用DCL创建单例对象时,不用volatile修饰对象,可能会导致2、3被重排序,被执行的实际步骤为1、3、2,在高并发的情况下可能会导致两个线程读到的值不一样,产生脏读。


  1. 主存:内存;每个线程在CPU上会有自己的缓存,内存是共享的 ↩︎

  2. DCL(Double Check Lock) 双重检查锁,懒汉式单例使用 ↩︎

你可能感兴趣的:(多线程与高并发)