目录 1、乐观锁和悲观锁的概念 2、synchronized底层的原理 3. CAS的原理 4. 并发包下Lock锁和synchronized对比 5. 锁升级原理 复制代码
大家好,我是四九城最豪横的小耳朵。
今天咱们来用大白话聊聊synchronized、CAS底层原理、Lock锁和锁升级原理。
比如线程A对某个变量进行修改,在这个修改期间,它持悲观心理,认为其他线程在这个期间,也有可能去修改这个变量,所以它就给变量加个锁,保证在它修改期间,别的线程没法去访问这个变量。这个锁就是悲观锁。悲观锁是重量级锁,代表对象synchronized关键字。
比如线程A对某个变量进行修改,在这个修改期间,它持乐观心理,认为其他线程在这个期间,不会去修改这个变量,所以它只在执行修改操作的时候,才会给变量加个锁。这个锁就是乐观锁。乐观锁是轻量级锁,代表对象CAS。
首先需要知道一个概念——monitor。
如果这个线程想获取monitor的锁,就先判断monitor的计数器是不是为0,如果为0,说明没人获取锁,这个线程就可以获取锁,然后对计数器加1;如果不为0,说明已经有其他线程已经获取了锁,这个线程就必须阻塞等待。
synchronized一般是对对象加锁,对类加锁也就是对类对象加锁。 如果使用了synchronized关键字,在底层编译后的jvm指令中, 会有monitorenter和monitorexit两个指令。线程进入synchronized代码片段,执行monitorenter指令对monitor计数器加1,这样其他线程发现monitor的计数器不为0,就阻塞等待;
上面的是针对synchronized对对象、类加锁的底层原理。
CAS,Conmpare And Swap,英文翻译过来就是“比较和交换”。它的过程是3步,第一步是读值,第二步比较值,看值和自己刚刚读的一不一样,第3步是修改,如果值跟自己读的一样,就修改。
CAS最经典的实现类就是AtomicInteger。
比如2个线程同时要对AtomicInteger加1,A线程读旧值,是0,B线程也读旧值,是0,A这时执行CAS操作,比较值,发现值和刚刚自己读的一样,都是0,然后它修改值为1;B线程执行CAS操作,比较值,发现值和刚刚自己读的不一样,变成1了,它相当于又读了一遍旧值,将自己内存中的旧值改为1,然后继续执行CAS操作。
我觉得二者的主要区别是以下四点;
1.首先synchronized是java内置关键字,是jvm层面,Lock是个java类,是jdk层面;
2.synchronized无法判断是否获取锁的状态,Lock可以判断是否获取到锁;
3.synchronized会自动释放锁,Lock需在finally中手工释放锁(unlock()方法释放锁),否则容易造成线程死锁;
4.Lock锁适合大量同步的代码的同步问题,synchronized锁适合代码少量的同步问题。
一开始是无锁的状态,一上来会先去判断一下有没有锁,有锁的话最开始的时候锁是支持偏向锁的。偏向锁当前获取到锁资源的这个线程,我会优先让他再去获取这个锁,如果它没获取到这个锁,就升级为一个轻量级的,一个cas锁,即乐观锁,乐观锁的时候它是一个比较和交换的过程,如果没有设置成功的话,它会进行一个自旋,然后自旋到一定次数之后才会升级成一个synchronized的这样一个重量级的锁,这样的话他就保证了性能的问题。你想想如果一开始就是synchronized这样一个重量级的锁,那性能就比较差了。
End 复制代码
作者简介:豪横的小耳朵,一个豪横的程序员。想和大家一起在技术的世界里豪横,用技术的眼光去看待世界。欢迎扫描下方二维码,持续关注,一大波原创系列文章正在路上