Java并发编程之一张图理解ReentrantLock
2021/5/23 1:25:23
本文主要是介绍Java并发编程之一张图理解ReentrantLock,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
一张图理解ReentrantLock
- 1.lock()跟踪源码
- 1.1.非公平锁实现
- 1.1.1.tryAcquire(arg)
- 1.1.2.acquireQueued(addWaiter(Node.EXCLUSIVE), arg)
- 1.2.公平锁实现
- 1.2.1.tryAcquire(arg)
首先看图。
1.lock()跟踪源码
这里对公平锁和非公平锁做了不同实现,由构造方法参数决定是否公平。
public ReentrantLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync(); }
1.1.非公平锁实现
static final class NonfairSync extends Sync { private static final long serialVersionUID = 7316153563782823691L; final void lock() { if (compareAndSetState(0, 1)) setExclusiveOwnerThread(Thread.currentThread()); else acquire(1); } protected final boolean tryAcquire(int acquires) { return nonfairTryAcquire(acquires); } }
代码量很少。首先compareAndSetState(0, 1)
通过CAS(期望值0,新值1,内存值stateOffset)
- 如果修改成功,即抢占到锁,
setExclusiveOwnerThread(Thread.currentThread());
将AQS中的变量exclusiveOwnerThread
设置为当前抢占到锁的线程,也就是图中的ThreadA。 - 若没有抢占成功,证明此时锁被占用,执行方法
acquire(1);
。
public final void acquire(int arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); }
这里主要看两个方法tryAcquire(arg)
和acquireQueued(addWaiter(Node.EXCLUSIVE), arg)
。当满足if条件后,会给当前线程标记一个interrupt
状态。
1.1.1.tryAcquire(arg)
这个方法又有多个实现。这里看NonfairSync
非公平锁。
protected final boolean tryAcquire(int acquires) { return nonfairTryAcquire(acquires); } final boolean nonfairTryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { if (compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); return true; } } else if (current == getExclusiveOwnerThread()) { int nextc = c + acquires; if (nextc < 0) // overflow throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } return false; }
在这个方法中,还不死心,首先会判断下AQS中的state是否为0,为0也就是说距离上次尝试获取锁到现在准备进入队列(双向链表)中这段时间内,锁已经被释放,可以重新CAS尝试获取锁。
如果当前锁还是被持有状态,就是state!=0
,就会判断,当前线程是不是当前持有锁的线程exclusiveOwnerThread
,如果是,则state+1
,从这里可以看出state表示的是重入次数。
全部不满足,返回false。
1.1.2.acquireQueued(addWaiter(Node.EXCLUSIVE), arg)
private Node addWaiter(Node mode) { Node node = new Node(Thread.currentThread(), mode); // Try the fast path of enq; backup to full enq on failure Node pred = tail; if (pred != null) { node.prev = pred; if (compareAndSetTail(pred, node)) { pred.next = node; return node; } } enq(node); return node; }
tryAcquire(arg)返回false,证明当前线程还是没有获取到锁。那么就要进入队列等待了,首先addWaiter
方法,将当前线程封装成一个Node,如果pred不为空,则将当前节点做链表的尾部插入,同时为了防止在此期间前序节点已经不在队列中了,也会运用CAS操作来执行(期望值pred,新值node,内存值tailOffset)。
如果前序节点为空,或者在CAS时发现前序节点已经不存在了,则重新构建链表,将当前节点封装的Node,加入到链表当中。
private Node enq(final Node node) { for (;;) { Node t = tail; if (t == null) { // Must initialize if (compareAndSetHead(new Node())) tail = head; } else { node.prev = t; if (compareAndSetTail(t, node)) { t.next = node; return t; } } } }
到这里,基于非公平锁的实现结束。
1.2.公平锁实现
公平锁和乐观锁的区别就在于,非公平锁acquire(1)
前会先尝试获取锁,公平锁
static final class FairSync extends Sync { private static final long serialVersionUID = -3000897897090466540L; final void lock() { acquire(1); } }
1.2.1.tryAcquire(arg)
在tryAcquire中也和非公平锁有一定的区别。在当前锁没有被占有时。非公平锁不用考虑目前AQS队列中的排队情况,直接通过CAS尝试获取锁。公平锁会看目前队列的状态,再来决定是尝试占有锁还是在队列中等待。
protected final boolean tryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); return true; } } else if (current == getExclusiveOwnerThread()) { int nextc = c + acquires; if (nextc < 0) throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } return false; }
未完待续…
这篇关于Java并发编程之一张图理解ReentrantLock的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-23Springboot应用的多环境打包入门
- 2024-11-23Springboot应用的生产发布入门教程
- 2024-11-23Python编程入门指南
- 2024-11-23Java创业入门:从零开始的编程之旅
- 2024-11-23Java创业入门:新手必读的Java编程与创业指南
- 2024-11-23Java对接阿里云智能语音服务入门详解
- 2024-11-23Java对接阿里云智能语音服务入门教程
- 2024-11-23JAVA对接阿里云智能语音服务入门教程
- 2024-11-23Java副业入门:初学者的简单教程
- 2024-11-23JAVA副业入门:初学者的实战指南