个人技术分享

并发编程第三周

1 锁的分类

1.1 可重入锁,不可重入锁

Java提供的synchronized,ReentrantLock,ReentrantReadWriteLock都是可重入锁

可重入:当前线程获取到A锁,在获取之后尝试再次获取A锁是可以直接拿到的。

不可重入:当前线程获取到A锁,在获取之后尝试再次获取A锁,无法获取到,因为A锁被当前线程占用着,需要等待自己释放锁再获取是锁。

1.2 乐观锁,悲观锁

Java提供的synchronized,ReentrantLock,ReentrantReadWriteLock都是悲观锁

Java中提供的CAS操作,就是乐观锁的一种实现

悲观锁:获取不到锁资源时,会将当前线程挂起(进入BLOCK,WAITING),线程挂起会涉及到用户态和内核态的切换,消耗资源

  • ​ 用户态:jvm可以自行执行的指令,不需要借助操作系统执行
  • ​ 内核态:jvm不可以自行执行的指令,需要借助操作系统执行

**乐观锁:**获取不到锁可以再让CPU调度,重新尝试获取锁资源

Atomic原子类就是基于CAS乐观锁实现的

1.3 公平锁,非公平锁

Java提供的synchronized只能时非公平锁

ReentrantLock,ReentrantReadWriteLock可以实现公平和非公平

**公平锁:**线程A获取到了锁资源,线程B没有拿到,线程B排队,线程C来了,锁被A持有,同时B在排队。直接排到B后面,等待B拿到锁资源或者B取消后,才可以尝试区竞争锁资源。

非公平锁:线程A获取到了锁资源,线程B没有拿到,线程B排队,线程C来了,直接先去尝试竞争一波

  • 拿到锁:插队成功
  • 没有拿到锁:依然排队到B后面,等待B拿到锁资源或者B取消后,才可以尝试区竞争锁资源。

1.4 互斥锁,共享锁

Java提供的synchronized,ReentrantLock 互斥锁

ReentrantReadWriteLock有互斥锁也有共享锁

互斥锁:同一时间点,只会有一个线程持有当前互斥锁

共享锁:同一时间点,当前共享锁可以被多个线程同时持有

2 深入synchronized

2.1 类锁,对象锁

synchronized的使用一般就是同步方法块和同步代码块

synchronizated的锁时基于对象实现的

使用同步方法:

  • static:此时使用的时当前类.class作为锁(类锁)
  • 非static:此时使用的时当前对象做为锁(对象锁)

2.2 synchronized的优化

锁消除:在synchronized修饰的代码中,如果不存在操作临界资源的情况,会触发锁消除,即便写了synchronized也不会触发

public synchronized void method(){
// 没有操作临界资源
// 此时这个方法的synchronized你可以认为木有~~
}

锁膨胀:如果在一个循环中,频繁的获取和释放锁资源,这样带来的消耗很大,锁膨胀就是将锁的范围扩大,避免,频繁的获取和释放锁资源带来不必要的消耗。

锁升级:ReentranLock的实现,是先基于乐观锁CAS尝试获取资源,如果拿不到锁资源,才会挂起线程。synchronizated在jdk1.6之前,完全就是获取不到锁,立即挂起当前线程,所以性能差。

synchronizated在jdk1.6之后做了锁升级的优化

  1. 无锁,匿名偏向:当前对象没有作为锁的存在

  2. 偏向锁:如果当前锁资源只有一个线程频繁的获取和释放,那么这个线程过来,只需要判断当前指向的线程是否位当前线程。

    • 如果是,直接拿走锁资源

    • 如果不是,基于CAS的方式尝试将偏向锁指向当前线程。如果获取不到,锁升级,升级位轻量级锁(偏向锁状态出现了锁竞争的情况)

  3. 轻量级锁:会采用自旋锁的方式取频繁的以CAS的形式获取锁资源。(采用的是自适应自旋锁)

    ​ 如果成功获取到,拿着锁资源走

    ​ 如果自旋一定次数,没有拿到锁资源,锁升级

  4. 重量级锁:就是最传统的synchronized方式,拿不到锁资源,就挂起当前线程。

2.3 synchronized实现原理

synchronized是基于对象实现的。

Java对象在堆内存中的存储

在这里插入图片描述

展开MarkWord

在这里插入图片描述

MarkWord中标记着四种锁的信息:无锁、偏向锁、轻量级锁、

偏向锁延迟:偏向锁在升级为轻量级锁时,会涉及到偏向锁撤销,需要等到一个安全点,才可以做偏向锁撤销,在明知道有并发情况下,就可以选择不开启偏向锁,或者设置偏向锁延迟开启。因为在jvm启动时,需要加载的.class文件到内存中这个操作会涉及到synchronized的使用,为了避免出现偏向锁撤销操作,jvm启动初期,会有一个延迟4s开启偏向锁的操作。如果正常开启偏向锁了,那么不会出现无锁状态,对象会直接变为匿名偏向

整个锁升级状态的转变:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

2.5 重量锁底层ObjectMonitor

在这里插入图片描述