偏向锁/轻量级锁/重量级锁

重量级锁会造成 CPU 在用户态和核心态之间频繁切换,所以代价高、效率低。JDK1.6 版本为了减少获得锁和释放锁所带来的性能消耗,引入了“偏向锁”和“轻量级锁”实现。所以,在 JDK1.6 版本里内置锁一共有四种状态:无锁状态、偏向锁状态、轻量级锁状态、重量级锁状态,这些状态随着竞争情况逐渐升级。内置锁可以升级但不能降级,意味着偏向锁升级成轻量级锁后不能降级成偏向锁。这种能升级却不能降级的策略,其目的是为了提高获得锁和释放锁的效率。

1. 无锁状态

Java 对象刚创建时,还没有任何线程来竞争,说明该对象处于无锁状态(无线程竞争它)

2. 偏向锁状态

偏向锁是指一段同步代码一直被一同个线程所访问,那么该线程会自动获取锁,降低获取锁
的代价。如果内置锁处于偏向状态,当有一个线程来竞争锁时,先用偏向锁,表示内置锁偏爱这
个线程,这个线程要执行该锁关联的同步代码时,不需要再做任何检查和切换。偏向锁在竞争不
激烈的情况下,效率非常高。

3. 轻量级锁状态

        当有两个线程开始竞争这个锁对象,情况发生变化了,不再是偏向(独占)锁了,锁会升级
为轻量级锁,两个线程公平竞争。当锁处于偏向锁的时候,而又被另一个线程所企图抢占时,偏向锁就会升级为轻量级锁。企图抢占的线程会通过自旋的形式尝试获取锁,不会阻塞抢锁线程,以便提高性能。

        自旋原理非常简单,如果持有锁的线程能在很短时间内释放锁资源,那么那些等待竞争锁的
线程就不需要做内核态和用户态之间的切换进入阻塞挂起状态,它们只需要等一等(自旋),等
持有锁的线程释放锁后即可立即获取锁,这样就避免用户线程和内核的切换的消耗。 但是线程自旋是需要消耗 CPU 的,如果一直获取不到锁,那线程也不能一直占用 CPU 自旋做无用功,所以需要设定一个自旋等待的最大时间。JVM 对于自旋周期的选择,JDK1.6 之后引入了适应性自旋锁,适应性自旋锁意味着自旋的时间不是固定的,而是由前一次在同一个锁上的自旋时间以及锁的拥有者的状态来决定。线程如果自旋成功了,则下次自旋的次数会更多,如果自旋失败了,则自旋的次数就会减少。如果持有锁的线程执行的时间超过自旋等待的最大时间扔没有释放锁,就会导致其他争用锁的线程在最大等待时间内还是获取不到锁,自旋不会一直持续下去,这时争用线程会停止自旋进入阻塞状态,该锁膨胀为重量级锁。

4. 重量级锁状态

重量级锁会让其他申请的线程之间进入阻塞,性能降低。重量级锁也就叫做同步锁

轻量级锁的很总要一个实现基础就是CAS操作(自旋):

CAS(Compare and swap),即比较并交换,也是实现我们平时所说的自旋锁或乐观锁的核心操作。

  • 执行函数:CAS(V,E,N) 其包含3个参数

    • V 表示要更新的变量
    • E 表示预期值
    • N 表示新值

如何要更新的变量等于预期值,就把新值赋值给变量,如何要更新的变量不等于预期值,就CAS再重新试一下,再试的时候,会重新读取要更新的变量作为预期值

比方说:
当前的这个线程想改这个值,我期望你是0,你就不能是1;如果是1,那就说明我这个值不对,然后想把你变成1。大概就是:原来这个值是变为3了,我这个线程想修改这个值的时候我一定期望你现在是3,是3我才改,如果在我修改的过程你变4了,说明就有另外一个线程修改过该值,那我cas就再重新试一下,再试的时候,我希望你的这个值是4,在修改的时候期望值是4,没有其它线程修改该值,那好我给你改成5,这样就是cas操作。

CAS 实现自旋锁

既然用锁或 synchronized 关键字可以实现原子操作,那么为什么还要用 CAS 呢,因为加锁或使用 synchronized 关键字带来的性能损耗较大,而用 CAS 可以实现乐观锁,它实际上是直接利用了 CPU 层面的指令,所以性能很高。

上面也说了,CAS 是实现自旋锁的基础,cascpu的原语支持,也就是说cascpu指令级别上的支持,中间不能被打断,不会造成所谓的数据不一致问题,以达到锁的效果,至于自旋呢,看字面意思也很明白,自己旋转,翻译成人话就是循环,一般是用一个无限循环实现。这样一来,一个无限循环中,执行一个 CAS 操作,当操作成功,返回 true 时,循环结束;当返回 false 时,接着执行循环,继续尝试 CAS 操作,直到返回 true。

其实 JDK 中有好多地方用到了 CAS ,尤其是 java.util.concurrent包下,比如 CountDownLatch、Semaphore、ReentrantLock 中,再比如 java.util.concurrent.atomic 包下,相信大家都用到过 Atomic* ,比如 AtomicBoolean、AtomicInteger 等。

这里拿 AtomicBoolean 来举个例子,因为它足够简单。

public class AtomicBoolean implements java.io.Serializable {private static final long serialVersionUID = 4654671469794556979L;// setup to use Unsafe.compareAndSwapInt for updatesprivate static final Unsafe unsafe = Unsafe.getUnsafe();private static final long valueOffset;static {try {valueOffset = unsafe.objectFieldOffset(AtomicBoolean.class.getDeclaredField("value"));} catch (Exception ex) { throw new Error(ex); }}private volatile int value;public final boolean get() {return value != 0;}public final boolean compareAndSet(boolean expect, boolean update) {int e = expect ? 1 : 0;int u = update ? 1 : 0;return unsafe.compareAndSwapInt(this, valueOffset, e, u); //这里cas,会触发cpu指令}}

ABA问题

CAS 存在一个问题,就是一个值从 A 变为 B ,又从 B 变回了 A,这种情况下,CAS 会认为值没有发生过变化,但实际上是有变化的。对此,并发包下倒是有 AtomicStampedReference 提供了根据版本号判断的实现,可以解决一部分问题。

参考文章:我们常说的 CAS 自旋锁是什么 - 风的姿态 - 博客园

【JUC系列】Java的锁机制相关推荐

  1. java锁的概念,Java ReentrantLock锁机制概念篇

    分享Java锁机制实现原理,细节涉及volatile修饰符.CAS原子操作.park阻塞线程与unpark唤醒.双向链表.锁的公平性与非公平性.独占锁和共享锁.线程等待await.线程中断interr ...

  2. JUC多线程:synchronized锁机制原理 与 Lock锁机制

    前言: 线程安全是并发编程中的重要关注点,造成线程安全问题的主要原因有两点,一是存在共享数据(也称临界资源),二是存在多条线程共同操作共享数据.因此为了解决这个问题,我们可能需要这样一个方案,当存在多 ...

  3. Java基础-锁机制

    Java中的锁机制 Lock不是Java语言内置的, synchronized是Java语言中的关键字, Lock是一个接口,通过这个接口可以实现同步访问. Lock允许更灵活的结构, 并可以支持多个 ...

  4. [java多线程] - 锁机制同步代码块信号量

    在美眉图片下载demo中,我们可以看到多个线程在公用一些变量,这个时候难免会发生冲突.冲突并不可怕,可怕的是当多线程的情况下,你没法控制冲突.按照我的理解在java中实现同步的方式分为三种,分别是:同 ...

  5. java线程锁机制_多线程之锁机制

    前言 在Java并发编程实战,会经常遇到多个线程访问同一个资源的情况,这个时候就需要维护数据的一致性,否则会出现各种数据错误,其中一种同步方式就是利用Synchronized关键字执行锁机制,锁机制是 ...

  6. JAVA之JUC系列 - JAVA内存模型

    Java内存模型(简称JMM),定义了线程本地内存和主内存之间的关系,理解JMM的特性,对深入理解Java多线程中内存的可见性会有很大帮助.下面我们从并发编程模型中关注的两个问题说起. 一. 并发编程 ...

  7. Java的锁机制--偏向锁、轻量锁、自旋锁、重量锁

    基础知识 线程切换代价 Java的线程是映射到操作系统的原生线程之上的,如果阻塞或唤醒一个线程就需要操作系统介入,需要在用户态和内核态之间切换,该切换会消耗大量的系统资源,因为用户态和内核态均有各自专 ...

  8. java线程 锁_Java多线程(二) 多线程的锁机制

    当两条线程同时访问一个类的时候,可能会带来一些问题.并发线程重入可能会带来内存泄漏.程序不可控等等.不管是线程间的通讯还是线程共享数据都需要使用Java的锁机制控制并发代码产生的问题.本篇总结主要著名 ...

  9. 关于锁机制:数据库锁

    一.什么是锁机制?数据库为什么要会有锁机制 数据库是一个多用户使用的共享资源.当多个用户并发地存取数据时,在数据库中就会产生多个事务同时存取同一数据的情况.若对并发操作不加控制就可能会读取和存储不正确 ...

最新文章

  1. APS:大型多模态室内摄像机定位系统
  2. Singleton Pattern
  3. [WebGL入门]十二,模型数据和顶点属性
  4. 蚂蚁前端的开局十年:一切都在路上
  5. 开发直播APP软件一定要了解的H.264编码,即时通讯中的战斗机
  6. kafka 0.9 java开发_kafka 0.9 java producer and consumer demo
  7. (又有惊喜)Redis5.0重量级特性Stream尝鲜
  8. Java是如何实现跨平台运行的
  9. 小D课堂-SpringBoot 2.x微信支付在线教育网站项目实战_5-1.数据信息安全--微信授权一键登录功能介绍...
  10. 【优化算法】非洲秃鹫优化算法(AVOA)【含Matlab源码 1805期】
  11. 考研高等数学第一讲手写笔记 函数、极限与连续
  12. iTween介绍和用法
  13. Ubuntu18.04 U盘安装和分区方案
  14. Win7 安装.Net 4.7.2 失败
  15. Word 软回车和硬回车
  16. 【Nodejs】用http模块写一个简单的web服务器
  17. 中医药大学计算机考试题,浙江中医药大学2013年级研究生《计算机应用》期末考试复习题...
  18. linux 安装字体库
  19. wps删除分节符导致前面格式变乱的解决方案
  20. HTML5 实现扫描识别二维码 生成二维码

热门文章

  1. javascript对于DOM加强
  2. 使用微软的TFS云服务来管理小型项目
  3. 2BizBox-ERP那点事儿系列之4
  4. 分别是什么意思_美国FBA头程:空派/海派分别是什么意思?
  5. python把dict转为dataframe,将python OrderedDict转换为datafram
  6. java 全局变量 内存不回收_JAVA知识梳理:内存管理与垃圾回收机制
  7. redis 后台运行_第一章 1.3Linux下安装Redis
  8. python如何通过以太网发送指令_用scapy在python中编写一个以太网桥
  9. mysql对库授权alter_mysql 数据库授权(给某个用户授权某个数据库)
  10. plsql登录时显示无服务器,plsql登录提示ORA-12514:TNS:listener does not currently know of service…………...