什么是悲观锁和乐观锁?

思维导图

什么是悲观锁和乐观锁?

文章插图
 
文章已收录Github精选 , 欢迎Star:https://github.com/yehongzhi/learningSummary
悲观锁悲观锁是平时开发中经常用到的一种锁 , 比如ReentrantLock和synchronized等就是这种思想的体现 , 它总是假设别的线程在拿线程的时候都会修改数据 , 所以每次拿到数据的时候都会上锁 , 这样别的线程想拿这个数据就会被阻塞 。如图所示:
什么是悲观锁和乐观锁?

文章插图
 
synchronized是悲观锁的一种实现 , 一般我们都会有这样使用:
private static Object monitor = new Object();public static void main(String[] args) throws Exception {    //锁一段代码块    synchronized (monitor){    }}//锁实例方法 , 锁对象是this , 即该类实例本身public synchronized void doSome(){}//锁静态方法 , 锁对象是该类 , 即XXX.classpublic synchronized static void add(){}我们以最简单的同步代码块来分析 , 其实就是将synchronized作用于一个给定的实例对象monitor , 即当前实例对象就是锁对象 , 每次当线程进入synchronized包裹的代码块时就会要求当前线程持有monitor实例对象锁 , 如果当前有其他线程正持有该对象锁 , 那么新到的线程就必须等待 , 这样也就保证了每次只有一个线程执行synchronized内包裹的代码块 。
从上面的分析中可以看出 , 悲观锁是独占和排他的 , 只要操作资源都会对资源进行加锁 。假设读多写少的情况下 , 使用悲观锁的效果就不是很好 。这时就引出了接下来要讲的乐观锁 。
乐观锁乐观锁 , 顾名思义它总是假设最好的情况 , 线程每次去拿数据时都认为别人不会修改 , 所以不会上锁 , 但是在更新的时候会判断一下在此期间别人有没有去更新这个数据 , 如果这个数据没有被更新 , 当前线程将自己修改的数据成功写入 。如果数据已经被其他线程更新 , 则根据不同的实现方式执行不同的操作(例如报错或者自动重试) 。如图所示:
什么是悲观锁和乐观锁?

文章插图
 
一般乐观锁在JAVA中是通过无锁编程实现的 , 最常见的就是CAS算法 , 比如Java并发包中的原子类的递增操作就是通过CAS算法实现的 。
CAS算法 , 其实就是Compare And Swap(比较与交换)的意思 。目的就是将内存的值更新为需要的值 , 但是有个条件 , 内存值必须与期待的原内存值相同 。展开来说 , 我们有三个变量 , 内存值M , 期望的内存值E , 更新值U , 只有当M==E时 , 才会将M更新为U 。
CAS算法实现的乐观锁在很多地方有应用 , 比如并发包的原子类AtomicInteger类 。在自增的时候就使用到CAS算法 。
public final int getAndIncrement() {    return unsafe.getAndAddInt(this, valueOffset, 1);}//var1 是this指针//var2 是偏移量//var4 是自增量public final int getAndAddInt(Object var1, long var2, int var4) {    int var5;    do {        //获取内存 , 称之为期待的内存值E        var5 = this.getIntVolatile(var1, var2);        //var5 + var4的结果是更新值U        //这里使用JNI方法 , 每个线程将自己内存中的内存值M与var5期望值比较 ,         //如果相同则更新为var5 + var4 , 返回true跳出循环 。        //如果不相同 , 则把内存值M更新为最新的内存值 , 然后自旋 , 直到更新成功为止    } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4)); //返回更新后的值    return var5;}public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);


推荐阅读