Java高并发——“锁”知识整理

最近在复习Java并发的时候,着重看了各类锁,了解了基本概念,现在将相关锁的知识点整理如下,供初学者快速入门理解。

乐观锁和悲观锁

  • 乐观锁:在并发下对数据进行修改时保持乐观的态度。认为在自己修改数据的过程中,其它线程不会对同一数据进行修改,所以不对数据加锁,但是在最终更新数据前,会判断一下这个数据有没有被修改,若没有,才将它更新为自己修改的值;
  • 悲观锁:在并发下对数据进行修改时保持悲观的态度。认为在自己修改数据的过程中,其它线程也会对同一数据进行修改,所以在操作前先对数据加锁,在操作完成后释放锁,而在释放锁之前,其它线程无法操作该数据。

CAS是乐观锁的一种实现方式,而悲观锁比较典型的是synchronized

独占锁和共享锁

  • 独占锁:又叫排它锁,指该锁只能被一个线程所拥有。如果线程T对数据A加上独占锁后,其它线程不能再对A加任何锁。获得排它锁的线程既能读数据又能修改数据。
    ReentrantLocksynchronized 以及JUC中的Lock都是独占锁
  • 共享锁:指该锁可被多个线程共享。如果线程T对数据A加上共享锁后,其它线程只能对A再加共享锁,不能加排它锁。获得共享锁的线程只能读数据,不能修改数据。

独占锁和共享锁都是通过AQS实现的;
ReentrantReadWriteLock:读锁(ReadLock)是共享锁,写锁(WriteLock)是独占锁。二者都是通过内部类Sync(AQS的一个子类)实现的锁。读锁的共享可以保证高并发性。

公平锁和非公平锁

  • 公平锁:多个线程会按照申请顺序去获得锁,线程直接进入队列去排队,永远都是队列的第一位才能获得锁。
  1. 优点:所有线程都能获得资源,不会饿死在队列中;
  2. 缺点:吞吐量会下降很多,除了第一个线程,其它线程都会阻塞,CPU唤醒线程的开销会很大。
  • 非公平锁:多个线程去获取锁时,获取不到再进入队列等待,如果能获取到,直接获取。
  1. 优点:CPU不必唤醒所有线程,减少了CPU唤醒线程的开销,整体的吞吐率会上升;
  2. 缺点:队列中的一些线程会因为长时间获取不到锁而饿死。

synchronized同步锁

有关synchronized同步锁在我之前的一篇博客里已经讲过,这里抛一下链接:synchronized与volatile关键字详解

锁升级

锁一共有四种状态:无锁、偏向锁、轻量级锁和重量级锁(逐渐升级)。锁的优缺点对比:

优点缺点适用场景
偏向锁加锁和解锁不需要额外消耗,与非同步锁方法仅存在微小差距如果线程之间存在锁竞争,会带来额外的锁撤销的消耗单线程访问同步块
轻量级锁竞争的线程不会产生阻塞,提高了程序响应速度始终得不到锁的线程自旋会消耗CPU追求响应时间,同步块执行速度非常快
重量级锁线程竞争不使用自旋消耗CPU线程阻塞,响应时间缓慢追求吞吐量,同步块执行时间较长

JVM锁优化

锁优化这块写的比较糙,主要参考的是这个博客:JVM锁优化,后期认真看完其它讲解后会补充详细~

自旋锁与自适应自旋

传统的独占锁会产生阻塞,线程的挂起和恢复需要转入内核态中完成,非常影响效率。

  • 自旋锁:多线程场景下,线程之间通过抢占CPU时间分片执行任务,有可能持有锁的线程很快就会释放锁,此时让后面的线程执行一个忙循环进行等待,这就是自旋锁。自旋次数默认为10次,可使用-XX:PreblockSpin参数来更改
  • JDK1.6加入了自适应自旋锁,这意味着自旋时间不再是固定的,而是由上一次在同一个锁上的自旋时间锁的拥有者状态来决定。

锁消除

锁消除是指在JVM即时编译器运行时,在对代码进行同步时,对检测到不存在共享数据竞争的锁进行消除。

锁粗化

如果一系列的连续操作都对同一个对象反复加锁和解锁,则将会把同步的范围粗化到整个操作序列的外部,这样只需要加一次锁就好了。

偏向锁

  • 目的:在无竞争的情况下消除整个同步使用的互斥量,连CAS操作都不做了。
  • 定义:偏向锁会偏向第一个获得它的线程,如果在接下来的执行过程中该锁没有被其它线程获取,则持有偏向锁的线程以后进入这个锁相关的同步块不再需要进行同步。
  • 原理:
    1.锁对象第一次被获取时,虚拟机会把对象头中的锁标志位设置为01,即偏向锁模式;
    2.同时使用CAS操作将线程ID记录在对象的 Mark World 之中;
    3.若CAS操作成功,则以后进入这个锁相关的同步块时不再进行任何同步操作。

当另一个线程尝试获取这个锁时,偏向模式结束。此时根据锁对象是否处于锁定状态,将锁对象恢复到未锁定状态或者轻量级锁状态(锁的升级,单向不可逆)。

轻量级锁

  • 目的:在无竞争的情况下使用CAS操作去消除同步使用的互斥量;
  • 原理:
    1.代码进入同步块的时候,若锁对象没有被锁定。
    2.虚拟机首先在栈帧中建立一个名为锁记录(Lock Record)的空间,存储锁对象的 Mark World 的拷贝;
    3.虚拟机使用CAS操作将对象的 Mark World 更新为指向 Lock Record 的指针;
    4.若这个CAS操作成功,则此对象处于轻量级锁定状态;
    5.若这个CAS操作失败,则检查对象的 Mark World 是否已经指向当前线程栈帧中的 Lock Record,
    6.若已经指向,则表明当前线程拥有该对象的锁,继续执行,否则膨胀为重量级锁。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值