Article reference: Miss Liu’s source code class
The complete code
MiniLock:
/ * * *@author csp
* @dateThe 2021-05-01 * /
public interface MiniLock {
/** * lock */
void lock(a);
/** * release lock */
void unlock(a);
}
Copy the code
\
MiniReentrantLock:
/ * * *@author csp
* @date2021-05-01 * MiniReentrantLock is an exclusive lock, fair lock, reentrant lock */
public class MiniReentrantLock implements MiniLock {
/** * lock state: =0, >0, * what is locked? -> Lock the thread resource */
private volatile int state;
/** * The thread currently owning the lock: * What is exclusive mode? * Only one thread can hold the lock at a time. Other threads will block if they do not acquire the lock */
private Thread exclusiveOwnerThread;
* 1. Head points to the node at the head of the queue -> the thread corresponding to the head node, which is the thread currently holding the lock! * 2. Tail Points to the node */ at the end of the queue
/** * Queue header */
private Node head;
/** * end of queue */
private Node tail;
/** * Blocked threads are encapsulated as Node nodes and placed in FIFO queues */
static final class Node {
// Front node reference
Node prev;
// Post-node reference
Node next;
// Encapsulate the thread
Thread thread;
public Node(a) {}public Node(Thread thread) {
this.thread = thread;
}
public Node(Node prev, Node next, Thread thread) {
this.prev = prev;
this.next = next;
this.thread = thread; }}/** * Get the lock: * if the current lock is occupied, it will block the calling thread until it preempts the lock. * * lock Lock process: * Scenario 1: when the thread executes, the current state == 0, the thread can directly grab the lock */ scenario 2: when the thread executes, the current state == 0, the thread needs to enqueue */
@Override
public void lock(a) {
// make the current thread compete for resources
// State = 1 when the lock is first acquired
// State = n when the lock is acquired NTH time
acquire(1);
}
/** * The method by which the current thread competes for resources; * 2. Try to obtain the lock, obtain the failure, block the current thread * *@param arg
*/
private void acquire(int arg) {
// Failed to obtain the lock
if(! tryAcquire(arg)) {// More complex logic comes ~
// Add the current thread to the blocking queue
Node node = addWaiter();
// Once enqueued, the current thread continues to compete for resources until it successfully acquires the lock
acquireQueued(node, arg);
}
// Attempt to acquire the lock succeeds. The current exclusive lock thread exclusiveOwnerThread and related properties execute ~ in tryAcquire
}
/** * What do I need to do when AN attempt to preempt the lock fails? * 1. Wrap the current thread as node and add it to the blocking queue * 2. Park the current thread so that it is suspended and waiting to be woken up * * What do YOU need to do when woken up? * 1. Check whether the current node is head.next * (head.next is a thread with preemption permission, and no other node has preemption permission) * 2. Preemption * If preemption succeeds, the current node is set to head, the old head is removed from the queue, and the business layer is returned * * if preemption fails, the current thread will continue to park, waiting to be woken up * * =====> * 1. Logical addWaiter() * 2 to add the current thread to the blocking queue. The current thread contention resource logic acquireQueued() */
/** * Add the current thread to the blocking queue: head is the lock holding node * addWaiter() after completion, ensure that the current thread has been queued successfully! * *@returnReturns the current thread's Node */
private Node addWaiter(a) {
// Create the current thread node node
Node newNode = new Node(Thread.currentThread());
// How to join the team?
// 1. Find the pred of newNode
// 2. Update newNode.prev = pred
// 3.CAS updates tail to newNode
// 4. Update pred.next = newNode
// In case 1, there are already waiting nodes in the queue (not empty), and the current node is not the first node to join the queue
Node pred = tail;
if(pred ! =null) {
newNode.prev = pred;
// If the condition is true, the current thread is queued successfully!
if (compareAndSetTail(pred, newNode)) {
pred.next = newNode;
returnnewNode; }}// There are two cases:
// 1.tail == null The queue is empty
// 2. Cas failed to set the current newNode to tail because another thread beat it to tail
// Spin into the team, only if the team successfully completed the spin:
eng(newNode);
return newNode;
}
/** * causes the current thread to compete for resources until it successfully acquires the lock@param node
* @param arg
*/
private void acquireQueued(Node node, int arg) {
// Only when the current node successfully acquired the lock will the spin exit:
for(; ;) {// Under what circumstances can a node attempt to acquire a lock after being awakened?
// Only if the current node is a successor of the head node.
Node pred = node.prev;
// the head node is the node currently entitled to hold the lock
if (pred == head /* If the condition is true, the current node has preemption permission */ && tryAcquire(arg) /* Try to get the lock */) {
// Enter if, the current thread contention lock successfully!
// What do I need to do next?
// 1. Set the current head to the node of the current thread
// 2. Assist the original head to exit the team
setHead(node);
pred.next = null;// help GC
return;
}
// If there is no lock preemption permission, the current thread suspends and continues to wait...
// park... Thread hanging
System.out.println("Thread:" + Thread.currentThread().getName() + "Hang up!);
LockSupport.park();
System.out.println("Thread:" + Thread.currentThread().getName() + "Wake up!); }}Tail == null is an empty queue * 2. Cas failed to set the current newNode to tail, which was preempted by another thread */
private void eng(Node node) {
/ / spin ~
for(; ;) {The current thread is the first thread to fail to preempt the lock
// The thread that currently holds the lock (note: the tryAcquire method directly acquirement the thread that holds the lock.
// The thread holding the lock is not set to any node.
// As the thread's first afterdrive, next needs to clean up its ass (add a node to the thread holding the lock and set it to the head of the blocking queue)
// the head node at any time represents the thread currently holding the lock.
if (tail == null) {
// If the compareAndSetHead condition is true, the current thread gave the current thread the lock, and the head operation succeeded!
if (compareAndSetHead(new Node())) {
// tail = head indicates that the current queue has only one element, where the current thread holding the lock is put into the blocking queue and is head ~
tail = head;
// The next time you enter the loop, the blocking queue is not empty, and head is the thread node holding the lock.}}else {
// Add node to queue if node is already in queue
// How to join the team? Same logic as addWaiter method
// 1. Find the pred of newNode
// 2. Update newNode.prev = pred
// 3.CAS updates tail to newNode
// 4. Update pred.next = newNode
// The queue already has a waiting node (not empty). The current node is not the first node to join the queue
Node pred = tail;
if(pred ! =null) {
node.prev = pred;
// If the condition is true, the current thread is queued successfully!
if (compareAndSetTail(pred, node)) {
pred.next = node;
// If you join the team, you must return the end of the infinite for loop ~
return;
}
}
}
}
}
/** * Attempts to acquire the lock without blocking the thread **@param arg
* @returnTrue - > try to obtain success | false lock - > attempt failed to get the lock * /
private boolean tryAcquire(int arg) {
// If the current locked state is 0
if (state == 0) {
// Condition 1:! HasQueuePredecessor () : indicates if there are no waiting threads in front of the current thread (to ensure fairness)
// Condition 2: compareAndSetState(0, ARG) : State was successfully modified using CAS
// If conditions 1 and 2 are true, the current thread succeeded in grabbing the lock.
if(! hasQueuePredecessor() && compareAndSetState(0, arg)) {
// Lock grab succeeded:
// 1. You need to set the exclusiveOwnerThread to the current thread in the IF block
this.exclusiveOwnerThread = Thread.currentThread();
// Succeeded in obtaining the lock
return true;
}
// If the current thread is the thread holding the lock and state > 0
} else if (Thread.currentThread() == this.exclusiveOwnerThread) {
// Is there concurrency in this if judgment? Does not exist -> because only the currently locked thread has permission to change state
// Get the lock status of the current thread
int s = getState();
s = s + arg;
// Cross the line... (omitted), in ReentrantLock, there must be an out-of-bounds judgment here
// Assign s to state to update the lock state
this.state = s;
// Succeeded in obtaining the lock
return true;
}
// Failed to obtain the lock:
// 1.CAS failed to lock and there is a waiting thread in front of the current thread
// 2. State > 0 and the current thread is not the thread that occupies the lock
return false;
}
/** * Method call chain: * lock -> acquire -> tryAcquire -> hasQueuedPredecessor (ps: state is 0, that is, the current lock is in the no master state..) * /
/** * Check whether the FIFO queue is empty: **@returnTrue - > said in front of the current thread is waiting for the thread | false - > said the current thread is waiting for the thread * /
private boolean hasQueuePredecessor(a) {
Node h = head;
Node t = tail;
Node s;
// When is false returned?
// 1. The current queue is empty
// 2. The current thread is the head. Next node thread, head
// Condition 1: h! = t
// Yes: the current queue already has node
H == t == null 2. h == t == head The first thread that fails to acquire the lock will create a head node for the thread that currently holds the lock.
/ / condition 2: ((s = h.n ext) = = null | | s.t hread! = Thread.currentThread())
// Rule out several cases:
// condition 2.1 :(s = h.ext) == null
// In extreme cases, the first thread that fails to acquire the lock will create a new head for the thread that holds the lock, and then automatically join the queue.
// The thread will return true if the head. Next node is present
returnh ! = t && ((s = h.next) ==null|| s.thread ! = Thread.currentThread()); }/** * release lock */
@Override
public void unlock(a) {
/ / releases the lock
release(1);
}
/** * Release lock method **@param arg
*/
private void release(int arg) {
// The tryRelease condition is true: the thread has released the lock completely
// What can I do for you?
// There are several sleeping threads in the blocking queue. Should I wake up a thread?
if (tryRelease(arg)) {
// Get the head node
Node head = this.head;
// You have to know, is there any waiting?
// If head. Next == null, there is no waiting,
// If head. Next! = null indicates that there is a waiting thread to wake up
if(head.next ! =null) {
// Wake up the head. Next nodeunparkSuccessor(head); }}}/** * wakes up the thread node **@param node
*/
private void unparkSuccessor(Node node) {
// node after head
Node s = node.next;
if(s ! =null&& s.thread ! =null) {
// The waiting thread wakes upLockSupport.unpark(s.thread); }}/ * * * in fact completely releases the lock methods * completely releases the lock is successful, it returns true | otherwise, shows the current state > 0, return false. * /
private boolean tryRelease(int arg) {
int c = getState() - arg;
if(getExclusiveOwnerThread() ! = Thread.currentThread()) {throw new RuntimeException("No! You must getLock!");
}
// What if I go here? Is there concurrency? Only one thread, the ExclusiveOwnerThread, is going to come in here.
// If the current thread holds the lock, the lock has been released completely.
if (c == 0) {
// What needs to be done?
// 1.ExclusiveOwnerThread set to null
// 2. Set state == 0
this.exclusiveOwnerThread = null;
this.state = c;
return true;
}
// state is not equal to 0, cannot release the lock, return false
this.state = c;
return false;
}
private void setHead(Node node) {
this.head = node;
// Why is thread null? The node is already the thread that acquired the lock
node.thread = null;
node.prev = null;
}
public int getState(a) {
return state;
}
public Thread getExclusiveOwnerThread(a) {
return exclusiveOwnerThread;
}
public Node getHead(a) {
return head;
}
public Node getTail(a) {
return tail;
}
/**
* 基于CAS,得到state、head、tail属性的setter方法
*/
private static final Unsafe unsafe;
private static final long stateOffset;
private static final long headOffset;
private static final long tailOffset;
static {
try {
Field f = Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
unsafe = (Unsafe) f.get(null);
stateOffset = unsafe.objectFieldOffset
(MiniReentrantLock.class.getDeclaredField("state"));
headOffset = unsafe.objectFieldOffset
(MiniReentrantLock.class.getDeclaredField("head"));
tailOffset = unsafe.objectFieldOffset
(MiniReentrantLock.class.getDeclaredField("tail"));
} catch (Exception ex) {
throw newError(ex); }}private final boolean compareAndSetHead(Node update) {
return unsafe.compareAndSwapObject(this, headOffset, null, update);
}
private final boolean compareAndSetTail(Node expect, Node update) {
return unsafe.compareAndSwapObject(this, tailOffset, expect, update);
}
protected final boolean compareAndSetState(int expect, int update) {
return unsafe.compareAndSwapInt(this, stateOffset, expect, update); }}Copy the code