1.悲观锁和乐观锁的基本概念

悲观锁:

  1. 总是认为当前想要获取的资源存在竞争(很悲观的想法),因此获取资源后会立刻加锁,于是其他线程想要获取该资源的时候就会一直阻塞直到能够获取到锁;
  2. 在传统的关系型数据库中,例如行锁、表锁、读锁、写锁等,都用到了悲观锁。还有java中的同步关键字Synchronized也是一种悲观锁;

乐观锁:

  1. 总是认为当前想要获取的资源不存在竞争(很乐观的想法),因此在获取资源后,并不会加锁;
  2. 但是在执行更新操作时,会判断在这期间是否有其他人更新过这个数据,可使用版本号等机制实现;
  3. 适用于多读的应用程序,可提高吞吐量;
  4. 像数据库提供的类似于write_condition机制,其实都是提供的乐观锁。在Java中java.util.concurrent.atomic包下面的原子变量类就是使用了乐观锁的一种实现方式CAS实现的。

2.乐观锁的一种实现方式:CAS

  因为乐观锁的思想是:在通常情况下都认为不会产生并发冲突,因此在对数据进行提交更新的时候,会对将要提交更新的数据进行并发冲突检测、如果冲突存在,则会返回错误信息给用户,让用户决定处理方式。

  基于乐观锁的思想,我们可以知道乐观锁实现的步骤包含两个部分:冲突检测和数据更新,而CAS就是其中一个典型的实现方式.

  CAS:Compare And Swap(比较并交换)

    CAS是一种乐观锁技术。当多个线程使用CAS尝试更新同一个变量时,只有一个线程能够成功更新,其他线程都会失败,但是失败的线程并不会挂起,而是被告知在此次竞争中失败并可再次尝试。

    CAS包含三个操作数:

    

  在JDK1.5中新增的java.util.concurrent包中的内容就是建立早CAS基础之上的,相对于Synchronized的阻塞式算法,CAS其实是一种非阻塞算法的实现,因此java.util.concurrent包中组件的性能大大提升。

  下面以java.util.concurrent中的AtomicInteger的getAndIncrement(该操作相当于变量自加) 为例,看一下在不加锁的情况下,如何保证线程安全:

public class AtomicInteger extends Number implements java.io.Serializable {
private volatile int value; public final int get() {
return value;
} public final int getAndIncrement() {
//自旋方式采用CAS来修改当前值,直到成功为止
for (;;) {
int current = get();
int next = current + 1;
if (compareAndSet(current, next))
return current;
}
} public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
}

如果compareAndSet(current, next)方法成功执行,则直接返回;如果线程竞争激烈,导致compareAndSet(current, next)方法一直不能成功执行,则会一直循环等待。

3.CAS存在的问题

  ①. ABA问题。因为CAS需要在操作值的时候检查下值有没有发生变化,如果没有发生变化则更新,但是如果一个值原来是A,变成了B,又变成了A,那么使用CAS进行检查时会发现它的值没有发生变化,但是实际上却变化了。ABA问题的解决思路就是使用版本号。在变量前面追加上版本号,每次变量更新的时候把版本号加一,那么A-B-A 就会变成1A-2B-3A。

  

  ②.循环时间开销大:因为CAS中存在自旋,当自旋长时间不成功时,会给CPU带来极大开销,如果CPU执行支持pause指令,效率能够得到提升。

    pause指令作用1:延迟流水线执行指令,(de-pipeline),使CPU不会消耗过多的执行资源,延迟的时间取决于具体实现的版本,在一些处理器上延迟时间是零。

    pause指令作用2:可以避免在退出循环的时候因内存顺序冲突(memory order violation)而引起CPU流水线被清空(CPU pipeline flush),从而提高CPU的执行效率。

  ③.只能保证一个共享变量的原子操作。当对一个共享变量执行操作时,我们可以使用循环CAS的方式来保证原子操作,但是对多个共享变量操作时,循环CAS就无法保证操作的原子性,这个时候就可以用锁,或者有一个取巧的办法,就是把多个共享变量合并成一个共享变量来操作。比如有两个共享变量i=2,j=a,合并一下ij=2a,然后用CAS来操作ij。从Java1.5开始JDK提供了AtomicReference类来保证引用对象之间的原子性,你可以把多个变量放在一个对象里来进行CAS操作。

  

4.CAS与Synchronized的选择

1、线程冲突严重时,使用CAS等乐观锁,自旋几率较大,会因为自旋浪费更多的CPU资源;此时使用Synchronized等悲观锁性能较好。
2、线程冲突较轻时,使用synchronized同步锁进行线程阻塞和唤醒切换以及用户态内核态间的切换操作额外浪费消耗cpu资源,而自旋概率较小,使用CAS性能高于同步锁。

  

java多线程系列3:悲观锁和乐观锁的更多相关文章

  1. java多线程----悲观锁与乐观锁

    java多线程中悲观锁与乐观锁思想 一.悲观锁 总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁(共享资源每次只给一个线 ...

  2. 多线程深入:乐观锁与悲观锁以及乐观锁的一种实现方式-CAS(转)

    原文:https://www.cnblogs.com/qjjazry/p/6581568.html 首先介绍一些乐观锁与悲观锁: 悲观锁:总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每 ...

  3. Java并发问题--乐观锁与悲观锁以及乐观锁的一种实现方式-CAS

    首先介绍一些乐观锁与悲观锁: 悲观锁:总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁.传统的关系型数据库里边就用到了很 ...

  4. Java中的锁-悲观锁、乐观锁,公平锁、非公平锁,互斥锁、读写锁

    总览图 如果文中内容有错误,欢迎指出,谢谢. 悲观锁.乐观锁 悲观锁.乐观锁使用场景是针对数据库操作来说的,是一种锁机制. 悲观锁(Pessimistic Lock):顾名思义,就是很悲观,每次去拿数 ...

  5. Java锁之乐观锁、悲观锁、自旋锁

    java锁分为三大类乐观锁.悲观锁.自旋锁 乐观锁:乐观锁是一种乐观思想,即认为读多写少,遇到并发写的可能性低,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别 ...

  6. Java多线程系列--“JUC锁”03之 公平锁(一)

    概要 本章对“公平锁”的获取锁机制进行介绍(本文的公平锁指的是互斥锁的公平锁),内容包括:基本概念ReentrantLock数据结构参考代码获取公平锁(基于JDK1.7.0_40)一. tryAcqu ...

  7. Java多线程系列--“JUC锁”04之 公平锁(二)

    概要 前面一章,我们学习了“公平锁”获取锁的详细流程:这里,我们再来看看“公平锁”释放锁的过程.内容包括:参考代码释放公平锁(基于JDK1.7.0_40) “公平锁”的获取过程请参考“Java多线程系 ...

  8. Java多线程系列--“JUC锁”10之 CyclicBarrier原理和示例

    概要 本章介绍JUC包中的CyclicBarrier锁.内容包括:CyclicBarrier简介CyclicBarrier数据结构CyclicBarrier源码分析(基于JDK1.7.0_40)Cyc ...

  9. Java多线程系列--“JUC锁”02之 互斥锁ReentrantLock

    本章对ReentrantLock包进行基本介绍,这一章主要对ReentrantLock进行概括性的介绍,内容包括:ReentrantLock介绍ReentrantLock函数列表ReentrantLo ...

随机推荐

  1. javascript--获取一个页面各个标签的数量

    获取一个页面各个标签的数量 document.getElementsByTagName('*')--获取所有的标签. var obj = document.getElementsByTagName(' ...

  2. Windows设置 .exe 开机自启动

    例如:想让Nginx开机自启动 C:\ProgramData\Microsoft\Windows\Start Menu\Programs\StartUp

  3. 2019.9.25使用BP和Hydra爆破相关的服务

    使用BP和Hydra爆破相关的服务. Hydra:九头蛇,开源的功能强大的爆破工具,支持的服务有很多,使用hydra爆破c/s架构的服务.使用bp爆破web登录端口. dvwa:web应用程序漏洞演练 ...

  4. poj2279 Mr. Young's Picture Permutations[勾长公式 or 线性DP]

    若干人左对齐站成最多5行,给定每行站多少个,列数从第一排开始往后递减.要求身高从每排从左到右递增(我将题意篡改了便于理解233),每列从前向后递增.每个人身高为1...n(n<=30)中的一个数 ...

  5. 我说CMMI之三:CMMI的构件--转载

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明.本文链接:https://blog.csdn.net/dylanren/article/deta ...

  6. 使用ajax时给ajax绑定上一个进度条的简单示例

    直接放代码. <%@ page contentType="text/html;charset=UTF-8" language="java" %> & ...

  7. docker安装mysql5.6和redis3.2

    环境:centos 6.8, docker version 1.7.1 1.docker安装mysql5.6 第一步:拉取镜像 docker pull mysql:5.6 第二步:docker run ...

  8. hexdump命令

    hexdump hexdump用于以16进制.10进制.8进制.ascii码形式打印文件内容,也可以是标准输入的内容. 用法 hexdump [选项] 文件 ... 选项 选项 参数 说明 -b   ...

  9. 信息提示框:MessageBox

    一 函数原型及参数 function MessageBox(hWnd: HWND; Text, Caption: PChar; Type: Word): Integer;   1.参数列表    hW ...

  10. 洛谷 P1546 最短网络 Agri-Net x

    题目背景 农民约翰被选为他们镇的镇长!他其中一个竞选承诺就是在镇上建立起互联网,并连接到所有的农场.当然,他需要你的帮助. 题目描述 约翰已经给他的农场安排了一条高速的网络线路,他想把这条线路共享给其 ...