First, the principle of internal
Class inheritance structure
Lock package related API inheritance structure, ignored some classes in order to observe its characteristics:
ReentrantLock and ReentrantReadWriteLock Lock interfaces are achieved by means of internal class Sync. ReentrantReadWriteLock not directly implement the interface but built-Lock lock -ReadLock read and write locks -WriteLock were realized Lock interface.
Sync comprises two subclasses: FairSync (Fair lock), NonfairSync (unfair locked); and in the construction of ReentrantLock ReentrantReadWriteLock objects can be specified by the constructor with a parameter whether to adopt a lock fair; fair non-default lock when not specified. Sync class is inherited from · AbstractQueuedSynchronizer.
Abstract queue synchronizer AQS
AbstractQueuedSynchronizer Acronym AQS, Doug Lea master creation, to construct the base frame locks or other synchronization class components, and in addition ReentrantLock ReentrantReadWriteLock many concurrent achieve other tools are dependent on the AQS; CountDownLatch, CyclicBarrier, Semaphore are all based on AQS achieve. (AQS chapter analyzes the source code, the next chapter can analyze usage of these classes).
AQS atomic management synchronization state, the thread and blocking and unblocking line provides a common mechanism. Template Design Method mode, and rewrites its synchronization logic method to acquire and release resources to tryAcquire () and tryRelease () by a combination of a subclass of AQS in use.
The idea is very simple to use AQS, when there is a thread to acquire the lock, call the subclass tryAcquire () tries to acquire a lock, on getting failed, the thread is added to a queue based on CLH lock structure implemented when the thread holding the lock when finished perform an operation to release the lock, call tryRelease subclass () to release the lock resources, and wake up the next thread waiting in the queue began to acquire the lock.
The main API
Synchronization status management
int getState (): Gets the synchronization status
void setState (): set up synchronization status
boolean compareAndSetState (int expect, int update): based on CAS, sets the current state atoms
Two exclusive mode for implementing the method for sharing and abstract patterns.
Exclusive lock operation
acquire (int): attempt to obtain competing for resources, if not acquired, the current thread will enter the waiting queue.
tryAcquire (): concrete realization of resource contention logic, have to be implemented by specific users of AQS.
acquireInterruptibly (): supports interrupt access to acquire operations
release (): the current thread release compete for resources, and wake up other threads waiting in the queue to enter the competition.
tryRelease (): release specific implementation compete for resources, it is necessary to achieve a specific user AQS
Shared mode lock operation
acquireShared (): acquire shared-mode version of.
tryAcquireShared (): shared-mode version of tryAcquire.
acquireSharedInterruptibly (): Sharing mode version of acquireInterruptibly
releaseShared (): Sharing mode version release of
tryReleaseShared (): Sharing mode version of tryRelease
Source
Internal waiting queue Node
static final class Node {
static final Node SHARED = new Node (); // sharing mode
static final Node EXCLUSIVE = null; // Exclusive
Node nextWaiter; // exclusive or shared mode flag
// wait state constants defined
static final int CANCELLED = 1; //取消
static final int SIGNAL = -1; // notify
static final int CONDITION = -2; // wait condition
static final int PROPAGATE = -3; // propagation
volatile int waitStatus; // wait state properties
volatile Node prev; // precursor node
volatile Node next; // successor node
volatile Thread thread; // node encapsulated thread
// determine whether the nodes in shared mode
final boolean isShared() {
return nextWaiter == SHARED;
}
// Get the precursor node, an exception is thrown is empty
final Node predecessor() throws NullPointerException {
Node p = prev;
if (p == null) {
throw new NullPointerException();
} else {
return p;
}
}
// Construct a thread node in exclusive or shared mode by addWaiter AQS call
Node(Thread thread, Node mode) {
this.nextWaiter = mode;
this.thread = thread;
}
Using this model constructor under // Condition
Node(Thread thread, int waitStatus) {
this.waitStatus = waitStatus;
this.thread = thread;
}
}
Get exclusive lock acquire ()
/**
*! TryAcquire (angry):
* First call its subclasses (actual users of AQS) tries to acquire the lock to succeed function directly returned.
* AcquireQueued (addWaiter (Node.EXCLUSIVE), arg)): the lock is not acquired subclass to:
* By addWaiter () in exclusive mode with the current thread and packaged into Node node performs necessary initialization.
* By acquireQueued () according to the position of the node in the queue, the implementation of access or the blocking thread lock operation
*/
public final void acquire(int arg) {
if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
/**
1. * the current node is node thread package.
* 2. uninitialized queue, the queue is initialized and the current node is added at the end of the thread queue.
* 3. If the queue is already initialized (nodes already exist other threads waiting for the lock), then the current thread node is added directly to the end of the team
* 4. Return Node node represents the current thread.
*/
private Node addWaiter(Node mode) {
Node node = new Node (Thread.currentThread (), mode); // current node Node packaged into thread.
Node pred = tail; // pred pointing end of the queue
if (pred! = null) {// if the queue is not empty at the end of element
node.prev = before;
// CAS update queue node point to the end of the current thread. After the update is successful, the queue after the end of the old thread drive points to the current node, node returns the current thread.
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq (node); // if the queue is empty, the queue is initialized, then the current node is queued thread
return node;
}
/**
* If the queue is not initialized, it is used as a non-pointing Node queue head, linked to the head node as the current thread end of the queue.
* As used herein, lightweight self-circulation + Cas update the way to deal with the issue enq function when multiple threads are executing concurrently,
* If you do not think so enq loop function is likely to bring any problems simultaneous multithreading?
*/
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;
}
}
}
}
/**
* If the node is the head of the queue precursor, an attempt to acquire the lock.
* In this case: node is the first valid node queue, a head is not in the initialization Node directed,
* Node after obtaining the lock by setHead (node) is set to become head, but the Thread nulling node and the predecessor node points, head still a nondirectional nodes)
* If the node is not the head of the queue precursor, the precursor will change the status of the current thread SIGNAL blocked by LockSupper.park ().
* At this point: Before node has at least two active nodes (node.prev and node.prev.prev) node.prev.prev at this time may have to acquire the lock, it could wait. But node.prev
* Certain waiting, then the current node should enter the block waiting, and waiting predecessor node is already in the state can be set to be set to notify the state.
*/
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;
}
After the precursor to ensure that the current node node status is Node.SIGNAL: // shouldParkAfterFailedAcquire ()
// parkAndCheckInterrupt (): by LockSupport.park () blocks the current thread.
if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
// get successful,
cancelAcquire(node);
}
}
/**
* If the current node after node status is Node.SIGNAL predecessor, returns true, otherwise, the precursor node status is set to Node.SIGNAL
* If you encounter predecessor node status is the case of cancellation, forward from predecessor node traversal to find the last node has not been canceled, connect the current node in the back of the node.
* / Zhengzhou which hospital is good flow http://www.120csfkyy.com/
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
// state has been set, it is safe
if (ws == Node.SIGNAL) return true;
if (ws > 0) {
// traverse forward from the current thread node, the node into the current thread to wait for a valid node of the queue by most.
do {
= = node.prev before pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
Exclusive release release the lock ()
/**
* First tone tryRelease () subclass resources released.
* If the head! = Null state or wait head is not zero, indicating that there is still waiting for a subsequent node. At this time, the subsequent node needs to wake up.
*/
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
/**
* The current node status is reset to zero, and to find a valid node from the first forward end of the queue, the node notified by the thread unpark. ,
* Why not look backward from the current node?
*/
private void unparkSuccessor(Node node) {
int ws = node.waitStatus;
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
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);
}
Share lock acquisition acquireShare
With exclusive mode of analysis, the sharing mode is much simpler.
/**
* Subclass tryAcquireShare call attempts to acquire a lock, tryAcquireShare () function returns the value is less than zero acquisition failure.
* When the acquisition fails, the call processing method doAcquireShared
*/
public final void acquireShared(int arg) {
if (tryAcquireShared(arg) < 0)
doAcquireShared (Arg);
}
/**
* Exclusive with similar processes, but is used when a node package SHARED mode.
*/
private void doAcquireShared(int arg) {
final Node node = addWaiter (Node.SHARED);
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head) {
int r = tryAcquireShared(arg);
if (r >= 0) {
setHeadAndPropagate(node, r);
p.next = null; // help GC
if (interrupted)
selfInterrupt();
failed = false;
return;
}
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt ())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
/**
* Call subclass release the lock, then the current node from the queue
*/
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
Shared lock release doReleaseShared
/**
* The actual execution of the operation to release the lock,
* Because it is a shared mode, there are cases where a plurality of threads concurrently released upon release of the lock mechanism thus employed + CAS cycle
* A plurality of threads to concurrently release order unpark subsequent node in the queue.
*/
private void doReleaseShared() {
for (;;) {
Node h = head;
if (h != null && h != tail) {
int ws = h.waitStatus;
if (ws == Node.SIGNAL) {
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue; // loop to recheck cases
unparkSuccessor(h);
}
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue; // loop on failed CAS
}
if (h == head) // loop if head changed
break;
}
}
You can interrupt mode acquireSharedInterruptibly
Interrupt mode is also acquired an exclusive lock, but will try to get a lock first before checking interrupt status of the thread, if the thread is interrupted, then throws InterruptedException and do not continue to try to acquire the lock. Interrupt mode also locks acquired) to be released by the release (.
/**
* When acquiring the lock will first determine whether the thread has been interrupted, if interrupt directly thrown.
* Not interrupted, call the subclass tryAcquire () tries to acquire the lock. When acquisition failure, by treatment doAcquireInterruptibly ().
*/
public final void acquireInterruptibly(int arg) throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (!tryAcquire(arg))
doAcquireInterruptibly (Arg);
}
/**
* And doAcquire () the same process, except that only the thread into the queue, while the other nodes on wake-up, will determine the subsequent operation based on the thread interrupt status:
* If the thread is interrupted, throw InterruptedException directly and not continue to acquire locks.
*/
private void doAcquireInterruptibly(int arg) throws InterruptedException {
final Node node = addWaiter(Node.EXCLUSIVE);
boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt ())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
AQS fundamental to this source would be finished.