乐观锁对应于生活中乐观的人总是想着事情往好的方向发展,悲观锁对应于生活中悲观的人总是想着事情往坏的方向发展。

一、引入概念

1、悲观锁

总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁(共享资源每次只给一个线程使用,其它线程阻塞,用完后再把资源转让给其它线程)。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。Java中synchronizedReentrantLock等独占锁就是悲观锁思想的实现。

2、乐观锁

总是假设最好的情况,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号机制和CAS算法实现。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库提供的类似于write_condition机制,其实都是提供的乐观锁。在Java中java.util.concurrent.atomic包下面的原子变量类就是使用了乐观锁的一种实现方式CAS实现的。

3、两种锁的使用场景

从上面对两种锁的介绍,我们知道两种锁各有优缺点,不可认为一种好于另一种,像乐观锁适用于写比较少的情况下(多读场景),即冲突真的很少发生的时候,这样可以省去了锁的开销,加大了系统的整个吞吐量。但如果是多写的情况,一般会经常产生冲突,这就会导致上层应用会不断的进行retry,这样反倒是降低了性能,所以一般多写的场景下用悲观锁就比较合适。

二、乐观锁的实现方式——CAS算法

1、CAS算法

什么是CAS?

CAS全称为Compare And Swap即比较并交换,其算法公式如下:

函数公式:CAS(V,E,N)V:表示要更新的变量E:表示预期值N:表示新值

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jpYccXsQ-1600049416983)(https://pics3.baidu.com/feed/810a19d8bc3eb135116a411f9e3739d6fc1f4497.jpeg?token=c57722e5bdc2b5d2e02a24affb2e2ac9&s=6AAC3C6203AEC5EF5CF530CE000080B1)]CAS

如果V值等于E值,则将V的值设为N。若V值和E值不同,则说明已经有其他线程做了更新,则当前线程什么都不做。通俗的理解就是CAS操作需要我们提供一个期望值,当期望值与当前线程的变量值相同时,说明还没线程修改该值,当前线程可以进行修改,也就是执行CAS操作,但如果期望值与当前线程不符,则说明该值已被其他线程修改,此时不执行更新操作,但可以选择重新读取该变量再尝试再次修改该变量,也可以放弃操作。

2、“ABA问题”与“版本号机制”

CAS 会导致“ABA 问题”。CAS 算法实现一个重要前提需要取出内存中某时刻的数据,而在下时刻比较并替换,那么在这个时间差类会导致数据的变化。

比如说一个线程 one 从内存位置 V 中取出 A,这时候另一个线程 two 也从内存中取出 A,并且two 进行了一些操作变成了 B,然后 two 又将 V 位置的数据变成 A,这时候线程 one 进行 CAS 操作发现内存中仍然是 A,然后 one 操作成功。尽管线程 one 的 CAS 操作成功,但是不代表这个过程就是没有问题的。

部分乐观锁的实现是通过版本号(version)的方式来解决 ABA 问题,乐观锁每次在执行数据的修改操作时,都会带上一个版本号,一旦版本号和数据的版本号一致就可以执行修改操作并对版本号执行+1 操作,否则就执行失败。因为每次操作的版本号都会随之增加,所以不会出现 ABA 问题,因为版本号只会增加不会减少。

3、Java中的CAS

JDK5增加java.util.concurrent包,其中很多类使用了CAS操作。这些CAS操作基于Unsafe类中的native方法实现:

//第一个参数o为给定对象,offset为对象内存的偏移量,通过这个偏移量迅速定位字段并设置或获取该字段的值,
//expected表示期望值,x表示要设置的值,下面3个方法都通过CAS原子指令执行操作,
//设置成功返回true,否则返回false。
public final native boolean compareAndSwapObject(Object o, long offset,Object expected, Object x);
public final native boolean compareAndSwapInt(Object o, long offset,int expected,int x);
public final native boolean compareAndSwapLong(Object o, long offset,long expected,long x);

由于CAS作用的对象在主存里而不是在线程的高速缓存里,CAS操作在Java中需要配合volatile使用。

Java中的CAS主要包含以下几个问题:

  • ABA问题,即变量V初次读时是A值,被赋值时也是A值,但期间变量被赋值成B值,CAS会误认为他从没被修改过。AtomicStampedReference和AtomicMarckableReference类提供了监测ABA问题的能力,其中的compareAndSet方法首先检查当前引用是否等于预期引用,并且当前标志等于预期标志,全部相等则以原子方式将该引用和该标志的值设置为给定的更新值。
  • 循环开销,自旋CAS长时间不成功会给CPU带来非常大的执行开销。若JVM能支持pause命令,效率有一定提升。因为pause命令一方面可以延迟流水线执行命令,使CPU不会消耗过多的执行资源,另一方面可以避免退出循环时由内存顺序冲突引起的CPU流水线被冲突,从而提高CPU的执行效率。
  • 只能保证一个共享变量的原子操作,当操作涉及跨多个共享变量时CAS无效。可用AtomicReference封装多个字段来保证引用对象之间的原子性。

三、悲观锁——synchronized

1、synchronized

synchronized是Java中的关键字,是一种同步锁。可修饰实例方法,静态方法,代码块。

  • 修饰实例方法:对当前实例加锁,进入同步代码前要获得当前实例的锁
  • 修饰静态方法:对当前类对象加锁,进入同步代码前要获得当前类对象的锁
  • 修饰代码块:指定加锁对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁。

2、CAS与synchronized的使用情景

  • 简单的来说CAS适用于写比较少的情况下(多读场景,冲突一般较少),
  • synchronized适用于写比较多的情况下(多写场景,冲突一般较多)
  • 对于资源竞争较少(线程冲突较轻)的情况,使用synchronized同步锁进行线程阻塞和唤醒切换以及用户态内核态间的切换操作额外浪费消耗cpu资源;而CAS基于硬件实现,不需要进入内核,不需要切换线程,操作自旋几率较少,因此可以获得更高的性能。
  • 对于资源竞争严重(线程冲突严重)的情况,CAS自旋的概率会比较大,从而浪费更多的CPU资源,效率低于synchronized。

补充: Java并发编程这个领域中synchronized关键字一直都是元老级的角色,很久之前很多人都会称它为 “重量级锁” 。但是,在JavaSE 1.6之后进行了主要包括为了减少获得锁和释放锁带来的性能消耗而引入的 偏向锁 和 轻量级锁 以及其它各种优化之后变得在某些情况下并不是那么重了。synchronized的底层实现主要依靠 Lock-Free 的队列,基本思路是 自旋后阻塞,竞争切换后继续竞争锁,稍微牺牲了公平性,但获得了高吞吐量。在线程冲突较少的情况下,可以获得和CAS类似的性能;而线程冲突严重的情况下,性能远高于CAS。

Java并发篇_乐观锁与悲观锁相关推荐

  1. JAVA并发篇_公平锁与非公平锁

    简单的来说,如果一个线程组里,能保证每个线程都能拿到锁,那么这个锁就是公平锁.相反,如果保证不了每个线程都能拿到锁,也就是存在有线程饿死,那么这个锁就是非公平锁. 一.引入概念 1.公平锁: 多个线程 ...

  2. Java并发篇_线程详解

    线程(thread) 是操作系统能够进行运算调度的最小单位.它被包含在进程之中,是进程中的实际运作单位.一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务 ...

  3. Java并发篇_进程线程

    一个进程包括由操作系统分配的内存空间,包含一个或多个线程.一个线程不能独立的存在,它必须是进程的一部分.一个进程一直运行,直到所有的非守护线程都结束运行后才能结束. 多线程能满足程序员编写高效率的程序 ...

  4. 学习笔记:Java 并发编程④_无锁

    若文章内容或图片失效,请留言反馈. 部分素材来自网络,若不小心影响到您的利益,请联系博主删除. 视频链接:https://www.bilibili.com/video/av81461839 配套资料: ...

  5. 学习笔记:Java 并发编程②_管程

    若文章内容或图片失效,请留言反馈. 部分素材来自网络,若不小心影响到您的利益,请联系博主删除. 视频链接:https://www.bilibili.com/video/av81461839 配套资料: ...

  6. 学习笔记:Java 并发编程⑥_并发工具_JUC

    若文章内容或图片失效,请留言反馈. 部分素材来自网络,若不小心影响到您的利益,请联系博主删除. 视频链接:https://www.bilibili.com/video/av81461839 配套资料: ...

  7. 学习笔记:Java 并发编程①_基础知识入门

    若文章内容或图片失效,请留言反馈. 部分素材来自网络,若不小心影响到您的利益,请联系博主删除. 视频链接:https://www.bilibili.com/video/av81461839 视频下载: ...

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

    Java并发问题–乐观锁与悲观锁以及乐观锁的一种实现方式-CAS </h1><div class="clear"></div><div c ...

  9. **Java有哪些悲观锁的实现_面试4连问:乐观锁与悲观锁的概念、实现方式、场景、优缺点?...

    推荐阅读: 数据库面试4连问:分库分表,中间件,优缺点,如何拆分? 终极手撕之架构大全:分布式+框架+微服务+性能优化,够不够? 消息队列面试,你能顶得住面试官这波10大连环炮的攻势吗? 01 乐观锁 ...

最新文章

  1. 5G:4G到5G的演进,整体网络架构的主要区别。
  2. react native 中下拉列表FlatList组件的讲解以及实例demo
  3. jquery 动态生成html后click事件不触发原因
  4. mysql导入数据,涉及到时间转换,乱码问题解决
  5. php显示表格,php – 显示所有表格行
  6. 1.7 Python基础知识 - 模块初识
  7. java撤销上一步_CAD快速入门技巧:CAD软件中撤销操作的方法汇总
  8. 《从0到1学习Flink》—— 如何自定义 Data Source ?
  9. C++变量/函数命名规范
  10. B站下载视频之you-get的使用
  11. 服务器能不能用普通硬盘,服务器硬盘与普通硬盘之间的区别
  12. 解决Layui表格头部工具栏事件绑定失效,上传文件按钮失效问题
  13. Android专业DJ,著名音乐游戏《DJ英雄》登陆Android Market
  14. JavaWeb网上图书商城
  15. CNN神经网络猫狗分类经典案例
  16. 爬虫很调皮?来看看反爬虫收拾爬虫的法子有哪些!
  17. 计算机怎么升级64位操作系统,32位系统怎么升级64位系统
  18. 计算机内存和外存的主要特点,内存与外存的主要特点
  19. Java jQuery_2
  20. UCSD异常检测数据集

热门文章

  1. 如何用Java代码解析json
  2. mysql root命令_MySQL 修改 root 密码命令
  3. 上传更新的代码到gitlab
  4. 记录qt窗口在拖动过程中出现的问题
  5. java中挂起和恢复,应用程序“未能及时恢复”并挂起
  6. 变频器输出功率_100米的深井泵,如何接变频器,怎样控制
  7. python解压到指定文件夹_在Python中压缩和解压文件
  8. 只能获取fixed语句初始值_因用了Insert into select语句,美女同事被开除了!
  9. android url 快捷方式,Android向桌面添加快捷方式,使其指向特定的(URL)网页
  10. linux中的进程权限是,Linux中权限,进程,服务的简单操作