【多线程】锁的七大分类

邓敏 2022年01月20日 136次浏览

锁的七大类

在Java的多线程中,有很多种锁,每种锁可能同时占有多个标准,比如ReentranLock即可是中断锁,又可以是可重入锁。

那么根据分类标准,我们可以把这些锁分为以下七大标准,分别是:

  • 偏向锁/轻量级锁/重量级锁
  • 可重入锁/非可重入锁
  • 共享锁/独占锁
  • 公平锁/非公平锁
  • 悲观锁/乐观锁
  • 自旋锁/非自旋锁
  • 可中断锁/不可中断锁

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

这三种锁特指 synchronized 锁的状态,通过在对象头种的mark word来表明锁的状态。

偏向锁

在被synchronized修饰的情况下,当前正在执行synchronized的线程,有且只有一个线程来获取synchronized的锁,这个时候系统就会给他升级为偏向锁,因为前来获取锁的线程并没有其他现在来竞争获取,所以系统只需要给他打一个标记,告诉它下一次的锁直接给他,这样就可以直接获得锁,并且开销也是最小的。

轻量级锁

满足轻量级锁的条件

  1. 被synchronized进行修饰的锁
  2. 被多个线程交替执行的,不是同时执行,不存在实际的竞争
  3. 只有短时间的锁竞争
    如果满足上面三种情况,synchronized锁的状态就会从偏向锁升级为轻量级锁,线程会通过自旋的形式来获取锁,而不会陷入阻塞

重量级锁

满足重量级锁的条件

  1. 被synchronized进行修饰的锁
  2. 被多个线程交替执行的并且是同时执行
  3. 锁的竞争时间较长
    重量级锁是互斥锁,他利用的是操作系统的同步机制来实现的,所以开销会比偏量锁和轻量锁的开销都大,当满足上面的条件后,轻量级锁就会升级为重量级锁,重量级锁就会让其他申请却拿不到锁的线程进入到阻塞状态。

锁升级路线

image.png
结合上面的描述,我们其实可以发现得到对于synchronized锁的升级路线,并且性能也是由着锁升级的推进,性能的开销也会变的越来越大。

可重入锁/非可重入锁

可重入锁:一个线程内可以同时获取一把锁多次
比如在线程A中有一把B锁,如果A在获取到B锁的情况下,再次去获取B锁,如果可以获取到B锁,那么他就是一把可重入锁。
例子:ReentrantLock

不可重入锁:一个线程内只能同时拥有一把锁一次
比如在线程A中有一把B锁,如果A线程在获取到B锁的情况下,想再次去获取到B锁却失败了,需要释放到之前的B锁,然后再去获取B锁,那么这样就是一个不可重入锁。

共享锁/独占锁

共享锁:可以同时被多个线程获取到锁,例如我们常见的读操作,它并不影响数据,所以可以被允许将锁给多个线程同时访问。
独占锁:有且只能被一个锁独占访问,这种就常用到我们的写操作上,由于需要保证数据的一致性和原子性。就需要在数据的写操作上只能允许一个线程来访问使用。

公平锁/非公平锁

公平锁:遵循先来先得的规则,如果线程在抢不到锁的情况下,会进入到排序的状态中,最先排队靠前的锁就会最显轮到获取锁。
非公平锁:在公平锁的规则基础上,会在一定的情况下导致锁插队。

自旋锁/非自旋锁

自旋锁的理念就是在线程拿不到锁的时候,它并不会进入到阻塞状态或者释放掉CPU的资源,它是直接去循环的去访问锁的状态,不断的去尝试获取锁,这个循环的过程也被叫做自旋。
非自旋锁就跟自旋锁相反,他没获取到锁他就不会再去尝试获取,但是他会直接去排队或者陷入阻塞状态中。

可中断锁/不可中断锁

顾名思义,可中断锁就是在获取到锁后可以中断他的任务然后去做其他的任务,最典型的就是使用ReentrantLock,它就是一种典型的可中断锁,例如使用lockInterruptibly方法再获取锁的过程中,突然不想获取了,那么也可以在中断之后去做其他的事情,不需要一直傻等到获取锁才离开

不可中断锁就没有那么灵活,典型的就是synchronized,一旦申请了锁,那么就没有回头路了,只能等到拿到锁以后才能进行其他的逻辑处理。