博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
乐观锁、悲观锁、公平锁、可重入锁
阅读量:4045 次
发布时间:2019-05-24

本文共 2357 字,大约阅读时间需要 7 分钟。

乐观锁

  • 认为数据在一般情况下不会造成冲突,所以在访问记录前不会加排它锁;

  • 在进行数据提交更新时,才会正式对数据冲突与否进行检测;

  • 乐观锁并不会使用数据库提供的锁机制,一般在表中添加version字段或者使用业务状态来实现;

  • 乐观锁直到提交时才锁定,所以不会产生任何死锁。

悲观锁

  • 指对数据被外界修改保持保守态度,认为数据很容易就被其他线程修改;

  • 在数据被处理前先对数据进行加锁,并在整个数据处理过程中,使数据处于锁定状态;

  • 悲观锁的实现往往依靠数据库提供的锁机制。

公平锁

  • 表示线程获取锁的顺序是按照线程请求锁的时间早晚来决定的,即最早申请锁的线程将最早获取到锁。

非公平锁

  • 在运行时闯入,根据具体调度方法确定顺序

  • 使用ReentrantLock方法来实现

独占锁

  • 保证任何时候都只有一个线程能得到锁,ReentrantLock就是以独占方式实现的。是一种悲观锁。

共享锁

  • 可以同时由多个线程持有,允许一个资源可以被多线程同时进行读操作。是一种乐观锁。

可重入锁

  • 当一个线程再次获取它自己已经获取的锁时不被阻塞,就说该锁时可重入的。

  • 原理:在锁内部维护一个线程标识,用来标识该锁目前被那个线程占用,然后关联一个计数器。

  • 计数器初始值为0,说明该锁没有被任何线程占用。当一个线程获取了该锁时,计数器的值为1,这时其他线程再来获取该锁时会发现所有者不是自己而被挂起。当获取了该锁的线程再次获取锁时发现锁拥有者是自己,则计数器值+1,释放锁后计数器值-1.当锁里面的表示被重置为null时,被阻塞的线程会被唤醒来竞争获取该锁。

自旋锁

  • 定义

    当前线程在获取锁时,如果发现锁已经被其他线程占有,它不马上阻塞自己,在不放弃CPU使用权的情况下,多次尝试获取,很有可能在后面几次尝试中其他线程已经释放了锁。

  • 缺点

    • 单核处理器上,不存在实际的并行,当前线程不阻塞自己的话,旧owner就不能执行,锁永远不会释放,此时不管自旋多久都是浪费;进而,如果线程多而处理器少,自旋也会造成不少无谓的浪费。

    • 自旋锁要占用CPU,如果是计算密集型任务,这一优化通常得不偿失,减少锁的使用是更好的选择。

    • 如果锁竞争的时间比较长,那么自旋通常不能获得锁,白白浪费了自旋占用的CPU时间。这通常发生在锁持有时间长,且竞争激烈的场景中,此时应主动禁用自旋锁。

自适应自旋锁

  • 自适应意味着自旋的时间不再固定了,而是由前一次在同一个锁上的自旋时间及锁的拥有者的状态来决定:

    • 如果在同一个锁对象上,自旋等待刚刚成功获得过锁,并且持有锁的线程正在运行中,那么虚拟机就会认为这次自旋也很有可能再次成功,进而它将允许自旋等待持续相对更长的时间,比如100个循环。

    • 相反的,如果对于某个锁,自旋很少成功获得过,那在以后要获取这个锁时将可能减少自旋时间甚至省略自旋过程,以避免浪费处理器资源。

  • 自适应自旋解决的是“锁竞争时间不确定”的问题。自适应自旋假定不同线程持有同一个锁对象的时间基本相当,竞争程度趋于稳定,因此,可以根据上一次自旋的时间与结果调整下一次自旋的时间。然而,自适应自旋也没能彻底解决该问题,如果默认的自旋次数设置不合理(过高或过低),那么自适应的过程将很难收敛到合适的值。

轻量级锁

  • 定义
    • 轻量级锁的目标是,减少无实际竞争情况下,使用重量级锁产生的性能消耗,包括系统调用引起的内核态与用户态切换、线程阻塞造成的线程切换等。
    • 使用轻量级锁时,不需要申请互斥量,仅仅将Mark Word中的部分字节CAS更新指向线程栈中的Lock Record,如果更新成功,则轻量级锁获取成功,记录锁状态为轻量级锁;否则,说明已经有线程获得了轻量级锁,目前发生了锁竞争(不适合继续使用轻量级锁),接下来膨胀为重量级锁。
  • 缺点
    同自旋锁相似,如果锁竞争激烈,那么轻量级将很快膨胀为重量级锁,那么维持轻量级锁的过程就成了浪费。

重量级锁

  • 内置锁在Java中被抽象为监视器锁(monitor)。在JDK 1.6之前,监视器锁可以认为直接对应底层操作系统中的互斥量(mutex)。这种同步方式的成本非常高,包括系统调用引起的内核态与用户态切换、线程阻塞造成的线程切换等。因此,后来称这种锁为“重量级锁”。

偏向锁

  • 定义

    • 偏向锁的目标是,减少无竞争且只有一个线程使用锁的情况下,使用轻量级锁产生的性能消耗。轻量级锁每次申请、释放锁都至少需要一次CAS,但偏向锁只有初始化时需要一次CAS。

    • 偏向锁假定将来只有第一个申请锁的线程会使用锁(不会有任何线程再来申请锁),因此,只需要在Mark Word中CAS记录owner(本质上也是更新,但初始值为空),如果记录成功,则偏向锁获取成功,记录锁状态为偏向锁,以后当前线程等于owner就可以零成本的直接获得锁;否则,说明有其他线程竞争,膨胀为轻量级锁。

  • 缺点

    如果明显存在其他线程申请锁,那么偏向锁将很快膨胀为轻量级锁。

小总结

  • 根据锁只能被单线程持有还是能被多个线程共同持有,可以分为独占锁和共享锁。

  • 偏向锁、轻量级锁、重量级锁适用于不同的并发场景:

    • 偏向锁:无实际竞争,且将来只有第一个申请锁的线程会使用锁。

    • 轻量级锁:无实际竞争,多个线程交替使用锁;允许短时间的锁竞争。

    • 重量级锁:有实际竞争,且锁竞争时间长。

      另外,如果锁竞争时间短,可以使用自旋锁进一步优化轻量级锁、重量级锁的性能,减少线程切换。如果锁竞争程度逐渐提高(缓慢),那么从偏向锁逐步膨胀到重量锁,能够提高系统的整体性能。

  • 锁的升级和降级

    所得升级、降级,就是JVM优化synchronized运行的机制,当JVM检测到不同的竞争状况时,会自动切换到适合的锁实现,这种切换就是锁的升级、降级。(如从偏向锁到轻量级锁再到重量级锁)

链接

  1. 《Java并发编程之美》

转载地址:http://uyzci.baihongyu.com/

你可能感兴趣的文章
关于进制转换的具体实现代码
查看>>
Golang 数据可视化利器 go-echarts ,实际使用
查看>>
mysql 跨机器查询,使用dblink
查看>>
mysql5.6.34 升级到mysql5.7.32
查看>>
dba 常用查询
查看>>
Oracle 异机恢复
查看>>
Oracle 12C DG 搭建(RAC-RAC/RAC-单机)
查看>>
Truncate 表之恢复
查看>>
Oracle DG failover 后恢复
查看>>
mysql 主从同步配置
查看>>
为什么很多程序员都选择跳槽?
查看>>
mongdb介绍
查看>>
mongdb安装使用
查看>>
mongdb在java中的应用
查看>>
区块链技术让Yotta企业云盘为行政事业服务助力
查看>>
Yotta企业云盘更好的为媒体广告业服务
查看>>
Yotta企业云盘助力旅游行业新发展
查看>>
Yotta企业云盘助力科技行业创高峰
查看>>
Yotta企业云盘更好地为教育行业服务
查看>>
Yotta企业云盘怎么帮助到能源化工行业
查看>>