Chapter 6 ReentrantLock source code parsing 2 -- release Lock unlock (), reentrantlock2 --
The most common method:
Int a = 12; // Note: This is usually set to a class variable, for example, the segment lock in the Segement and the global lock final ReentrantLock lock in the copyOnWriteArrayList = new ReentrantLock (); lock. lock (); // get the lock try {a ++; // business logic} catch (Exception e) {} finally {lock. unlock (); // release the lock}
NOTE: For the source code parsing of the lock () method, refer to "Chapter 5 ReentrantLock source code parsing 1 -- obtain unfair lock and fair lock ()". The specific link is as follows:
Http://www.cnblogs.com/java-zhao/p/5131544.html
Release lock: unlock ()
Steps:
1) obtain the current number of locks, then use the number of locks minus the number of locks (1 here), and finally obtain the result c.
2) checks whether the current thread is an exclusive lock thread. If not, an exception is thrown.
3) If c = 0, the lock is successfully released, the current exclusive thread is set to null, the number of locks is set to 0, and true is returned.
4) if c! = 0 indicates that the lock failed to be released. If the number of locks is set to c, false is returned.
5) if the lock is released successfully, wake up a non-canceled node closest to the first node.
Source code:
ReentrantLock: unlock ()
/*** Release the lock ** 1) if the current thread holds the lock, the number of locks is decreased * 2) if the number of locks is decreased to 0, the lock is released. * If the current thread does not have a persistent lock, an exception is thrown */public void unlock () {sync. release (1 );}View Code
AbstractQueuedSynchronizer: release (int arg)
/*** Release lock (in exclusive mode) */public final boolean release (int arg) {if (tryRelease (arg )) {// if the lock Node is successfully released h = head; // get the first Node :( Note: The first Node here is the Node currently releasing the lock) if (h! = Null & h. waitStatus! = 0) // the header node exists and the waiting state is not to cancel unparkSuccessor (h); // wake up a non-canceled node closest to the header node and return true;} return false ;}View Code
Sync: tryRelease (int releases)
/*** Release lock */protected final boolean tryRelease (int releases) {int c = getState ()-releases; // obtain the current number of locks-the number of incoming locks (1 here) if (Thread. currentThread ()! = GetExclusiveOwnerThread () // The current thread does not hold the lock throw new IllegalMonitorStateException (); boolean free = false; if (c = 0) {// The lock is released free = true; setExclusiveOwnerThread (null);} // if it is not 0, what should I do? Will it be released? SetState (c); return free ;}View Code
AbstractQueuedSynchronizer: unparkSuccessor (Node node)
/*** Wake up a non-canceled node closest to the first node Node * @ param node header node */private void unparkSuccessor (node Node) {int ws = node. waitStatus; if (ws <0) // set ws to 0 (that is, none) compareAndSetWaitStatus (node, ws, 0 ); /** get the Node whose next waiting status is not cancel */node s = Node. next; // if (s = null | s. waitStatus> 0) {s = null;/** Note: you can find a non-canceled node closest to the header node from the previous traversal, traversing from the back is said to be in the queue (enq (), maybe nodeX. next = null, but I didn't see it when I was reading the source code */for (Node t = Tail; t! = Null & t! = Node; t = t. prev) if (t. waitStatus <= 0) s = t;} if (s! = Null) LockSupport. unpark (s. thread); // wake up a non-canceled node closest to the header node}View Code
Note:
I have some questions about the program comments. I will organize them as follows:
- If the program starting with a lock is obtained successfully, the lock will be resolved once in finally. How can this problem be solved?
- When a non-canceled node closest to the header node is found, it is carried out from the back to the back, because "traversing from the back to the back is said to be in the queue (enq ()) nodeX. next = null ", but it is not displayed when reading the source code
Answer to the first question:
Reentrant is reflected in the following Program (lock, the most common is in recursion ):
Final ReentrantLock lock = new ReentrantLock (); public void add () {lock. lock (); // get the lock try {add (); // business logic} catch (Exception e) {} finally {lock. unlock (); // release the lock }}View Code
Note:
- The above program is just an example. In recursive use, there must be conditions for Recursive termination.
- Each lock () method corresponds to an unlock (). Therefore, when unlocking, you only need to set the number of passed locks to 1.
Answer to the second question:
Remember: It may be incorrect if you keep going next along the node header.
For example, A1> A2> A3
Now let's start from A1. When we get to A3, a new node, A4, enters the queue and goes down with the following code:
We can see that steps 1 and 2 are not atomic, that is, when CAS is executed but 2 is not executed, the queue is A1 --> A2 --> A4, if you use next, you may lose A3, but node. prev = t (A4.prev = A3), that is to say, the precursor node has been assigned a value. If you end the A4.prev from the queue, it will be A3, that is, A3 will not be lost.