MySQL Partition Tables: The Key to Optimizing Large Database Performance

How to use ReentrantLock

ReentrantLock is usually used as a member variable of a class for locking and releasing locks. Here is an example

 
 

java

copy code

class X { private final ReentrantLock lock = new ReentrantLock(); // ... public void m() { lock.lock(); // block until condition holds try { // ... method body } finally { lock.unlock(); } } }

ReentrantLock Generally used as a member variable,  lock the lock is acquired through the method. If the lock is not acquired, it will be blocked. After the lock is acquired, the business logic is executed and  finally the lock is released in the code to prevent deadlock.

ReentrantLock constructor

Let's first look at  ReentrantLock the constructor of the class

 
 

java

copy code

public class ReentrantLock implements Lock, java.io.Serializable { private static final long serialVersionUID = 7373984872572414699L; /** Synchronizer providing all implementation mechanics */ private final Sync sync; // 省略部分代码 /** * Creates an instance of {@code ReentrantLock}. * This is equivalent to using {@code ReentrantLock(false)}. */ public ReentrantLock() { sync = new NonfairSync(); } /** * Creates an instance of {@code ReentrantLock} with the * given fairness policy. * * @param fair {@code true} if this lock should use a fair ordering policy */ public ReentrantLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync(); } // 省略部分代码 }

ReentrantLock provides two construction methods. The default construction method implements an unfair lock internally  NonfairSync. You can also specify whether to create a fair lock  FairSync or an unfair lock  through parameters NonfairSync.

static inner class Sync

Sync Let's first look at the structure of the static inner class 

 
 

java

copy code

public class ReentrantLock implements Lock, java.io.Serializable { private static final long serialVersionUID = 7373984872572414699L; /** Synchronizer providing all implementation mechanics */ private final Sync sync; /** * Base of synchronization control for this lock. Subclassed * into fair and nonfair versions below. Uses AQS state to * represent the number of holds on the lock. */ abstract static class Sync extends AbstractQueuedSynchronizer { private static final long serialVersionUID = -5179523762034025860L; /** * Performs {@link Lock#lock}. The main reason for subclassing * is to allow fast path for nonfair version. */ abstract void lock(); /** * Performs non-fair tryLock. tryAcquire is implemented in * subclasses, but both need nonfair try for trylock method. */ 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; } 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; } protected final boolean isHeldExclusively() { // While we must in general read state before owner, // we don't need to do so to check if current thread is owner return getExclusiveOwnerThread() == Thread.currentThread(); } final ConditionObject newCondition() { return new ConditionObject(); } // Methods relayed from outer class final Thread getOwner() { return getState() == 0 ? null : getExclusiveOwnerThread(); } final int getHoldCount() { return isHeldExclusively() ? getState() : 0; } final boolean isLocked() { return getState() != 0; } /** * Reconstitutes the instance from a stream (that is, deserializes it). */ private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { s.defaultReadObject(); setState(0); // reset to unlocked state } } // 省略部分代码 }

SyncFairSync It is an abstract class that provides two implementations by default, which are the fair lock and unfair lock   we mentioned above  NonfairSync. The fair lock and unfair lock acquire locks in different ways. The fair lock will first determine whether there are other threads in the waiting queue In the queue, if there is, it will join the tail of the waiting queue, if not, it will try to  CAS acquire the lock, if the lock cannot be obtained, it will join the tail of the waiting queue. When the non-fair lock acquires the lock, it directly  CAS tries to acquire the lock, regardless of whether there are other threads in the waiting queue.

Sync Inheritance  AbstractQueuedSynchronizer is AbstractQueuedSynchronizerwhat we often call  AQS classes.

AQS (AbstractQueuedSynchronizer)

Inside is a  int status field  state and a FIFO waiting queue (doubly linked list), which state is equal to 0 means that no thread holds the lock, state and greater than 0 means that a thread holds the lock.

Implementation of ReentrantLock#lock unfair lock

Let's  ReentrantLock#lock follow up the code step by step through the method

 
 

java

copy code

public class ReentrantLock implements Lock, java.io.Serializable { private static final long serialVersionUID = 7373984872572414699L; /** Synchronizer providing all implementation mechanics */ private final Sync sync; // 省略部分代码 /** * Acquires the lock. * * <p>Acquires the lock if it is not held by another thread and returns * immediately, setting the lock hold count to one. * * <p>If the current thread already holds the lock then the hold * count is incremented by one and the method returns immediately. * * <p>If the lock is held by another thread then the * current thread becomes disabled for thread scheduling * purposes and lies dormant until the lock has been acquired, * at which time the lock hold count is set to one. */ public void lock() { sync.lock(); } // 省略部分代码 }

We can see that   a method of a subclass  lock is called inside the method  . Let's first look at  the implementation of unfair locks SynclockNonfairSync

 
 

java

copy code

public class ReentrantLock implements Lock, java.io.Serializable { private static final long serialVersionUID = 7373984872572414699L; /** Synchronizer providing all implementation mechanics */ private final Sync sync; // 省略部分代码 /** * Sync object for non-fair locks */ static final class NonfairSync extends Sync { private static final long serialVersionUID = 7316153563782823691L; /** * Performs lock. Try immediate barge, backing up to normal * acquire on failure. */ final void lock() { if (compareAndSetState(0, 1)) setExclusiveOwnerThread(Thread.currentThread()); else acquire(1); } protected final boolean tryAcquire(int acquires) { return nonfairTryAcquire(acquires); } } // 省略部分代码 public void lock() { sync.lock(); } // 省略部分代码 }

The unfair lock first  compareAndSetState tries to change  state the value of the value from 0 to 1 through the method. If the modification is successful, the current thread is set as the thread holding the lock.

 
 

java

copy code

final void lock() { if (compareAndSetState(0, 1)) setExclusiveOwnerThread(Thread.currentThread()); else acquire(1); }

AQS No, the method of  the parent class is being executed  acquire , we continue to follow up   the method AbstractQueuedSynchronizer in the class acquire

 
 

java

copy code

/** * Acquires in exclusive mode, ignoring interrupts. Implemented * by invoking at least once {@link #tryAcquire}, * returning on success. Otherwise the thread is queued, possibly * repeatedly blocking and unblocking, invoking {@link * #tryAcquire} until success. This method can be used * to implement method {@link Lock#lock}. * * @param arg the acquire argument. This value is conveyed to * {@link #tryAcquire} but is otherwise uninterpreted and * can represent anything you like. */ public final void acquire(int arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); }

First call  tryAcquire the method, you can see that  the method AbstractQueuedSynchronizer in the class tryAcquire is a  protected modified method, and an exception is thrown directly inside, then it is obvious that this method needs to be implemented by a subclass (template method mode)

 
 

java

copy code

/** * Attempts to acquire in exclusive mode. This method should query * if the state of the object permits it to be acquired in the * exclusive mode, and if so to acquire it. * * <p>This method is always invoked by the thread performing * acquire. If this method reports failure, the acquire method * may queue the thread, if it is not already queued, until it is * signalled by a release from some other thread. This can be used * to implement method {@link Lock#tryLock()}. * * <p>The default * implementation throws {@link UnsupportedOperationException}. * * @param arg the acquire argument. This value is always the one * passed to an acquire method, or is the value saved on entry * to a condition wait. The value is otherwise uninterpreted * and can represent anything you like. * @return {@code true} if successful. Upon success, this object has * been acquired. * @throws IllegalMonitorStateException if acquiring would place this * synchronizer in an illegal state. This exception must be * thrown in a consistent fashion for synchronization to work * correctly. * @throws UnsupportedOperationException if exclusive mode is not supported */ protected boolean tryAcquire(int arg) { throw new UnsupportedOperationException(); }

Then we go to  NonfairSync the class and see that  tryAcquire the method calls   the method Sync of  the parent classnonfairTryAcquire

 
 

java

copy code

/** * Performs non-fair tryLock. tryAcquire is implemented in * subclasses, but both need nonfair try for trylock method. */ final boolean nonfairTryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { // 当前没有线程持有锁 if (compareAndSetState(0, acquires)) { // 通过 CAS 去抢锁 // 抢到锁了,抢持有锁的线程 exclusiveOwnerThread 设置为当前线程 setExclusiveOwnerThread(current); return true; } } // 当前已经有线程获取到了锁,判断持有锁到线程是不是自己 else if (current == getExclusiveOwnerThread()) { // 持有锁的线程是自己(可重入锁) int nextc = c + acquires; if (nextc < 0) // overflow throw new Error("Maximum lock count exceeded"); // 将 state 值+1 setState(nextc); return true; } // 没有获取到锁,返回 false return false; }

So far, the implementation AQS in the class  tryAcquire has been analyzed. If the lock is not acquired, the conditional judgment in the if will continue to be executed, and we continue to analyze the  AQS class  addWaiter method

 
 

java

copy code

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

addWaiter Method implementation, if the lock is not acquired, the current thread needs to be added to the waiting queue

 
 

java

copy code

/** * Creates and enqueues node for current thread and given mode. * * @param mode Node.EXCLUSIVE for exclusive, Node.SHARED for shared * @return the new node */ private Node addWaiter(Node mode) { // 将当前线程封装到 Node 实例 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 指向前面node node.prev = pred; if (compareAndSetTail(pred, node)) { // 通过 CAS 将 tail 指向新加入的 node pred.next = node; // 将前面node.next 指向新node return node; } } // 尾指针为 null 的情况 enq(node); return node; }

AQScompareAndSetTail method  in class 

 
 

java

copy code

/** * CAS tail field. Used only by enq. */ private final boolean compareAndSetTail(Node expect, Node update) { return unsafe.compareAndSwapObject(this, tailOffset, expect, update); }

AQSenq method  in class 

 
 

java

copy code

/** * Inserts node into queue, initializing if necessary. See picture above. * @param node the node to insert * @return node's predecessor */ private Node enq(final Node node) { for (;;) { // for 循环,通过 CAS 将新node加入到链表尾部 Node t = tail; if (t == null) { // Must initialize if (compareAndSetHead(new Node())) // 通过 CAS 初始化 head 结点 tail = head; // 将 tail 指向 head } else { // 以下逻辑与 addWaiter 方法上面实现一致 node.prev = t; if (compareAndSetTail(t, node)) { t.next = node; return t; } } } }

So far, the thread that has not acquired the lock has been successfully added to the end of the waiting queue, and AQS the class  addWaiter method will return the node newly added to the waiting queue

 
 

java

copy code

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

We continue to analyze  acquireQueued the method

 
 

java

copy code

/** * Acquires in exclusive uninterruptible mode for thread already in * queue. Used by condition wait methods as well as acquire. * * @param node the node * @param arg the acquire argument * @return {@code true} if interrupted while waiting */ final boolean acquireQueued(final Node node, int arg) { boolean failed = true; try { boolean interrupted = false; for (;;) { // 自旋 final Node p = node.predecessor(); // 新加入node的前一个结点 // p == head 说明新加入的node是链表中的第一个数据结点,尝试获取锁 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); } }

Through the for loop spin, get the previous node of the current node, if the previous node is the head node, then try to acquire the lock, if the lock is acquired successfully. The head node of the update queue is the current node. Before the head points to an empty Node node (new Node()), here the thread and prev of the current node node are set to null, and the head points to the current node.

 
 

java

copy code

/** * Sets head of queue to be node, thus dequeuing. Called only by * acquire methods. Also nulls out unused fields for sake of GC * and to suppress unnecessary signals and traversals. * * @param node the node */ private void setHead(Node node) { head = node; // 将当前结点设置为头结点 node.thread = null; node.prev = null; }

If the node newly added to the waiting queue is not the first node, or is the first node but fails to acquire the lock, the method will be executed. This shouldParkAfterFailedAcquire method is used to determine whether the current thread needs to park after failing to acquire the lock ( hang).

 
 

java

copy code

/** * Checks and updates status for a node that failed to acquire. * Returns true if thread should block. This is the main signal * control in all acquire loops. Requires that pred == node.prev. * * @param pred node's predecessor holding status * @param node the node * @return {@code true} if thread should block */ private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) { int ws = pred.waitStatus; if (ws == Node.SIGNAL) /* * This node has already set status asking a release * to signal it, so it can safely park. */ return true; if (ws > 0) { /* * Predecessor was cancelled. Skip over predecessors and * indicate retry. */ do { node.prev = pred = pred.prev; } while (pred.waitStatus > 0); pred.next = node; } else { /* * waitStatus must be 0 or PROPAGATE. Indicate that we * need a signal, but don't park yet. Caller will need to * retry to make sure it cannot acquire before parking. */ compareAndSetWaitStatus(pred, ws, Node.SIGNAL); } return false; }

If the current thread needs to be suspended, execute  parkAndCheckInterrupt the method

 
 

java

copy code

/** * Convenience method to park and then check if interrupted * * @return {@code true} if interrupted */ private final boolean parkAndCheckInterrupt() { LockSupport.park(this); return Thread.interrupted(); }

Implementation of ReentrantLock#unlock unfair lock

 
 

java

copy code

/** * Attempts to release this lock. * * <p>If the current thread is the holder of this lock then the hold * count is decremented. If the hold count is now zero then the lock * is released. If the current thread is not the holder of this * lock then {@link IllegalMonitorStateException} is thrown. * * @throws IllegalMonitorStateException if the current thread does not * hold this lock */ public void unlock() { sync.release(1); }

AQS the  release method called 

 
 

java

copy code

/** * Releases in exclusive mode. Implemented by unblocking one or * more threads if {@link #tryRelease} returns true. * This method can be used to implement method {@link Lock#unlock}. * * @param arg the release argument. This value is conveyed to * {@link #tryRelease} but is otherwise uninterpreted and * can represent anything you like. * @return the value returned from {@link #tryRelease} */ public final boolean release(int arg) { if (tryRelease(arg)) { // 释放锁成功,获取头结点 Node h = head; if (h != null && h.waitStatus != 0) // 唤醒等待队列中的其他线程 unparkSuccessor(h); return true; } return false; }

AQS The  called  method tries to release the lock. The method  in  tryRelease the same   method has not been implemented specifically. This method needs to be implemented by subclasses. We enter   the class to view the specific implementationAQStryReleaseSync

 
 

java

copy code

protected final boolean tryRelease(int releases) { int c = getState() - releases; if (Thread.currentThread() != getExclusiveOwnerThread()) // 判断持有锁的线程是否是当前线程,如果不是抛异常 throw new IllegalMonitorStateException(); boolean free = false; if (c == 0) { // 如果state 的值为0,表示锁释放,对于可重入锁,加锁和解锁要成对出现 free = true; // 将持有锁的线程设置为 null setExclusiveOwnerThread(null); } // 更新 state 的值为0 setState(c); return free; }

Release the lock successfully, obtain the head node of the waiting queue, and wake up other threads in the waiting queue

 
 

java

copy code

/** * Wakes up node's successor, if one exists. * * @param node the node */ private void unparkSuccessor(Node node) { /* * If status is negative (i.e., possibly needing signal) try * to clear in anticipation of signalling. It is OK if this * fails or if status is changed by waiting thread. */ int ws = node.waitStatus; if (ws < 0) compareAndSetWaitStatus(node, ws, 0); /* * Thread to unpark is held in successor, which is normally * just the next node. But if cancelled or apparently null, * traverse backwards from tail to find the actual * non-cancelled successor. */ Node s = node.next; if (s == null || s.waitStatus > 0) { s = null; for (Node t = tail; t != null && t != node; t = t.prev) if (t.waitStatus <= 0) s = t; } if (s != null) LockSupport.unpark(s.thread); }

This article mainly introduces the underlying implementation principle of ReentrantLock unfair lock. You can check the source code for the implementation of fair lock.

Guess you like

Origin blog.csdn.net/BASK2312/article/details/131125689