This article brings you an introduction to the implementation principles of ReentrantLock (code examples). It has certain reference value. Friends in need can refer to it. I hope it will be helpful to you.
In concurrent programming, in addition to the synchronized keyword, ReentrantLock and ReentrantReadWriteLock in java.util.concurrent.locks in the java concurrency package are also commonly used lock implementations. This article analyzes the principle of reentrant lock from the source code.
Let’s first talk about reentrant locks: After a thread obtains the lock, it can obtain the lock multiple times without blocking itself.
ReentrantLock is implemented based on the abstract class AbstractQueuedSynchronizer (hereinafter referred to as AQS).
Look at the source code:
First of all, it can be seen from the constructor that ReentrantLock has two mechanisms: fair lock and unfair lock.
//默认非公平锁 public ReentrantLock() { sync = new NonfairSync(); } public ReentrantLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync(); }
First briefly explain the difference between fair locks and unfair locks, and then analyze the different implementation methods of the two.
Fair lock: Multiple threads are first-come, first-served. Similar to queuing, threads coming later are placed at the end of the queue.
Unfair lock: compete for locks. If it is grabbed, it will be executed. If it is not grabbed, it will be blocked. Wait for the thread that acquired the lock to be released before participating in the competition.
So unfair locks are usually used. Its efficiency is higher than fair lock.
Get lock
Fair lock
final void lock() { acquire(1); } public final void acquire(int arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); }
The first step is tryAcquire(arg) try to add Lock, implemented by FairSync, the specific code is as follows:
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; }
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; }
final boolean acquireQueued(final Node node, int arg) { boolean failed = true; try { boolean interrupted = false; for (;;) { final Node p = node.predecessor(); if (p == head && tryAcquire(arg)) { setHead(node); p.next = null; // help GC failed = false; return interrupted; } if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) interrupted = true; } } finally { if (failed) cancelAcquire(node); } }
Unfair lock
Unfair lock has differences in lock acquisition strategies.final void lock() { if (compareAndSetState(0, 1)) setExclusiveOwnerThread(Thread.currentThread()); else acquire(1); } 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; }Compared with the fair lock, the unfair lock tries to acquire the lock. There is no need to determine whether there are other threads in the queue.
Release lock
The steps for releasing locks are the same for fair locks and unfair lockspublic void unlock() { sync.release(1); } public final boolean release(int arg) { if (tryRelease(arg)) { Node h = head; if (h != null && h.waitStatus != 0) unparkSuccessor(h); return true; } return false; } //更新state protected final boolean tryRelease(int releases) { int c = getState() - releases; if (Thread.currentThread() != getExclusiveOwnerThread()) throw new IllegalMonitorStateException(); boolean free = false; if (c == 0) { free = true; setExclusiveOwnerThread(null); } setState(c); return free; }It is worth noting that because it is a reentrant lock, in the tryRelease() method, the state needs to be updated to 0 before the lock is considered to be completely released. After releasing, wake up the suspended thread.
The above is the detailed content of Introduction to the implementation principle of ReentrantLock (code example). For more information, please follow other related articles on the PHP Chinese website!