干货ReentrantLock非公平锁源码分析

开发 前端
ReentrantLock 是 Lock 的一种实现,是一种可重入的公平或非公平锁。默认是非公平锁。

[[339001]]

本文转载自微信公众号「Java极客技术」,作者鸭血粉丝。转载本文请联系Java极客技术公众号。   

1.锁

java中,加锁的方式

  1. synchronized,这个是 java 底层实现的,也就是 C 语言实现的。
  2. . lock,这个是 java.util.concurrent 包下面的,是 java语言实现的。

2.ReentrantLock

ReentrantLock 是 Lock 的一种实现,是一种可重入的公平或非公平锁。默认是非公平锁。

2.1 Lock的创建

首先看下锁的创建和使用代码:

  1. //创建锁 
  2. Lock lock  = new ReentrantLock(); 
  3. //加锁 
  4. lock.lock(); 
  5. //释放锁 
  6. lock.unlock(); 

然后看下创建的是 ReentrantLock 的构造函数:

  1. public ReentrantLock() { 
  2.     sync = new NonfairSync(); 

NonfairSync 就是非公平锁。所以 ReentrantLock 默认是非公平锁的实现

2.2 lock()

加锁的逻辑就比较复杂了,因为存在线程竞争。所以有两种情况,一种是竞争到锁的处理,一种是没有竞争到锁的处理。

首先我们还是来看下 lock() 方法,因为最终是非公平的实现,所以直接看 NonfairSync 里面的 lock 方法。

  1. final void lock() { 
  2.     if (compareAndSetState(0, 1)) 
  3.         setExclusiveOwnerThread(Thread.currentThread()); 
  4.     else 
  5.         acquire(1); 

2.3 没有获取到锁的逻辑 acquire()

直接上代码:

  1. public final void acquire(int arg) { 
  2.     if (!tryAcquire(arg) && 
  3.         acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) 
  4.         selfInterrupt(); 

还是3个方法,阿粉一个一个的说。

tryAcquire(arg) ,还是先看代码在分析。

  1. final boolean nonfairTryAcquire(int acquires) { 
  2.     final Thread current = Thread.currentThread(); 
  3.     int c = getState(); 
  4.     if (c == 0) { 
  5.         if (compareAndSetState(0, acquires)) { 
  6.             setExclusiveOwnerThread(current); 
  7.             return true
  8.         } 
  9.     } 
  10.     else if (current == getExclusiveOwnerThread()) { 
  11.         int nextc = c + acquires; 
  12.         if (nextc < 0) // overflow 
  13.             throw new Error("Maximum lock count exceeded"); 
  14.         setState(nextc); 
  15.         return true
  16.     } 
  17.     return false

a. 获取 state ,如果等于0,说明之前获得锁的线程已经释放了,那么这个线程就会再次去竞争锁,这就是非公平锁的体现,如果是公平锁,是没有这个判断的。

b. 如果前一个获得锁的线程没有释放锁,那么就判断是否是同一个线程,是的话就会将 state 加 1。这个就是重入锁的体现。

c. 如果都不满足,那么返回 false。

acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) ,再次获取锁没有成功,并且又不是可重入锁,那么就存入一个阻塞队列里面。里面还有一点逻辑,就不展开了,有兴趣可以自己看下。

selfInterrupt(); 这个是当前线程的中断标志,作用就是在线程在阻塞的是否,客户端通过调用了中断线程的方法 interrupt(),那么该线程被唤醒的时候,就会有响应的处理。具体要看这个线程 run 方法里面的代码逻辑。

2.4 unlock()

  1. protected final boolean tryRelease(int releases) { 
  2.     int c = getState() - releases; 
  3.     if (Thread.currentThread() != getExclusiveOwnerThread()) 
  4.         throw new IllegalMonitorStateException(); 
  5.     boolean free = false
  6.     if (c == 0) { 
  7.         free = true
  8.         setExclusiveOwnerThread(null); 
  9.     } 
  10.     setState(c); 
  11.     return free

state - 1,如果大于0,说明释放的是重入锁,只需要修改 state 就行了

如果等于0,说明要释放锁,释放锁首先需要把独占线程设置为null,再把state设置为0。

3 总结

Lock 锁的实现:

互斥性:需要一个状态来判断是否竞争到锁:state 并且需要用 volatile修饰,保证线程之间的可见性。

可重入性:Thread exclusiveOwnerThread 这个成员变量来记录当前获得锁的线程。

公平或非公平:默认非公平,NonfairSync。

没有竞争到锁的线程怎么办?放到队列中。

没有竞争到锁的线程怎么释放CPU?park:阻塞线程释放CPU资源,这个操作在 acquireQueued(),阿粉没没有讲这个。

最后来张流程图:

 

责任编辑:武晓燕 来源: Java极客技术
相关推荐

2022-12-26 00:00:04

公平锁非公平锁

2022-07-12 08:56:18

公平锁非公平锁Java

2019-01-04 11:18:35

独享锁共享锁非公平锁

2022-05-09 07:37:04

Java非公平锁公平锁

2023-10-07 08:17:40

公平锁非公平锁

2018-07-31 15:05:51

Java公平锁线程

2021-08-20 07:54:20

非公平锁 Java多线编程

2022-11-14 11:09:36

源码AQS加锁

2021-07-02 08:51:09

Redisson分布式锁公平锁

2021-06-30 14:56:12

Redisson分布式公平锁

2021-07-01 09:42:08

Redisson分布式

2021-06-02 21:31:39

Synchronous非公平模式

2022-06-15 15:14:17

Java公平锁非公平锁

2020-11-09 07:29:12

ReentrantLo源码公平锁

2022-12-08 17:15:54

Java并发包

2024-01-29 15:54:41

Java线程池公平锁

2021-05-11 14:50:21

ReentrantLo可重入锁Java

2023-08-28 07:49:24

Redisson锁机制源码

2022-12-31 09:42:14

超时功能

2023-07-06 08:06:47

LockCondition公平锁
点赞
收藏

51CTO技术栈公众号