JAVA锁机制

锁机制

一、介绍

锁机制是用来实现线程间同步的基础,并非是JAVA中独有的概念。本篇主要讲JAVA中的锁,按照锁分类,有以下几种锁

  • 公平锁/非公平锁
  • 可重入锁/不可重入锁
  • 独占锁/共享锁
  • 互斥锁/读写锁
  • 乐观锁/悲观锁
  • 分段锁
  • 偏向锁/轻量级锁/重量级锁
  • 自旋锁

但这些锁并不完全指的是锁,有的是锁设计,有的是锁状态,也有的是锁的特性

下面就介绍一下关于这些锁


二、公平锁/非公平锁

1. 公平锁

就是指当多个线程都要申请锁时,需要按照申请锁的先后顺序来进行获取。

2. 非公平锁

指当多个线程申请锁时,无法确定哪一个线程会获取锁,与申请的先后顺序无关

💡 非公平锁相较于公平锁,吞吐量上会更具优势。JAVA中的synchronized关键字和ReentrantLock都是非公平锁,但ReentrantLock是通过AQS实现的线程调度,所以可以**使ReentrantLock变为公平锁;**synchronized则没有办法变为公平锁。


三、可重入锁/不可重入锁

1. 可重入锁

顾名思义就是说可以重复获取的锁,但仅仅针对同一个的线程。对于同一个线程来说,如果该对象已经获取到了该锁并且没有释放,那么它可以继续获取该锁而不会造成死锁。

2. 不可重入锁

与可重入锁相反,即使是同一个线程,在持有锁的状态下继续申请该锁,也同样造成死锁的问题。也就是说这种锁不可以递归调用,一旦递归调用就会造成死锁。

💡 ReentrantLock翻译过来就是可重入锁,所以听名字就知道他是可重入的,而synchronized实际上也是一个可重入锁

如何判断一个锁是否使可重入锁呢?

class Test{
	public void a(){
		ReentrantLock lock = new ReentrantLock(true);
		lock.lock();
		// 重复调用锁不会造成死锁即是不可重入锁
		lock.lock();
	}
}

四、独占锁/共享锁

1. 独占锁

在同一时间只有一个线程可以获取到这个锁,也就是说不会存在两个线程同时持有一把锁的情况,当一个线程获取到锁,其他线程就会阻塞。

2. 共享锁

允许有多个线程同时获取到该锁,不同线程获取同一把锁时不会阻塞。


五、互斥锁/读写锁

1. 互斥锁

互斥锁的效果和概念类似于独占锁,都是在同一时刻只能有一个线程获取到锁。(我不太清楚互斥锁和独占锁之间是否有区别,有知道的朋友可以留言告诉我一下。)

2. 读写锁

读写锁就是一种互斥锁。我们知道多线程读文件不会导致并发问题,但多线程写就存在并发问题。所以在读-读的场景下是不存在互斥关系的,但在读-写,写-写的场景下是存在互斥关系的,当一个线程持有了写锁,那么其他任何线程都不能获取读锁或者写锁。

💡 读写锁是一种特殊的独占锁,因为读-读的场景下并不存在互斥关系,而读-写,写-写的场景下存在互斥关系,所以读写锁更适用于读多写少的场景。


六、乐观锁/悲观锁

1. 乐观锁

乐观锁实际上并不是锁,他总是乐观的认为在他要修改数据的时候,其他线程不会进行修改。仅仅只是在更新数据的时候判断数据是否被更改。我们一般使用CAS算法+版本号进行实现。

💡 单纯使用CAS算法的话存在ABA问题,也就是说数据从A变到了B,又从B变了A,这时候CAS算法就会认为数据没有发生变更。所以我们需要添加版本号解决该问题,每次对数据的操作都会让版本号+1,从而解决该问题。

2. 悲观锁

悲观锁和乐观锁相反,他悲观的认为每次修改数据都存在其他线程同时修改,于是每次修改数据都会对资源上锁,阻塞其他线程。

💡 JAVA中synchronized和ReentrantLock都是悲观锁。


七、分段锁

分段锁不是一种实际存在的锁,他是一种锁的设计思想。有的时候我们仅仅只是需要修改部分数据,但我们却需要将整个对象锁住,这会导致一定的性能问题,而如果我们只是对需要修改的那一段数据进行加锁的话,效率就会高很多。但是分段锁也并不是越多越好,当我们需要进行统计的时候,我们就需要获取到所有的分段锁才可以进行统计。


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

这几个锁也并非是实际的锁,而是一种JAVA中锁的状态。JAVA中锁从低到高有以下几个状态

  • 无锁
  • 偏向锁
  • 轻量级锁
  • 重量级锁

这几个锁状态是JVM为了提高锁的获取效率而提出来的,使用在synchronized中。

1. 锁升级

我们常说的锁升级就是指在竞争锁的过程中,根据竞争的激烈程度,锁的状态发生由低到高的转变,且这种转变过程是不可逆的。这种现象被称为锁升级。

锁状态竞争锁状况描述
偏向锁基本无竞争若一段代码一直被同一个线程执行,则这个线程就会自动获得这个锁。
轻量级锁较少竞争若锁处于偏向锁状态,且有另一个线程申请该锁,则升级为轻量级锁,其他线程会通过自旋的方式进行等待,不会阻塞。
重量级锁较多竞争当锁处于偏向锁状态,且其他线程自旋的次数达到一个阈值时,就会升级为重量级锁,对其他线程进行阻塞,性能会有所下降。

九、自旋锁

自旋可以理解为自己原地转圈等待,而在代码里的体现形式就是循环。CAS算法其实就是一种自旋的实现。

在CAS中有几个值V,E,N。V是当前值,E是期望的变量值,N是变量的新值。当且仅当V==E时才会将N复制给V。

开始
V==E
CompareAndSet
结束
更新E

十、总结

锁机制的目的是解决线程安全问题,而使用锁机制必然会导致性能的降低,因为从并发变为了同步。悲观锁、独占锁等会导致线程阻塞,比较重,为了尽可能的减少对性能的影响,就出现了乐观锁,自旋锁,通过其他方式来达到线程安全的目的,但这种锁一般只使用在并发量不高的情况下。因为读写的特性,可以存在同时读,但不能存在同时写,于是有了读写锁。

每种锁的出现都有它的意义,对于我们上层开发人员来说难的不是使用锁,而是判断在什么情况下应该使用哪种锁,在解决并发问题的同时尽可能的减少对性能的影响。

  • 4
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值