Objective
Locks are used to control how multiple threads access a shared resource. Before the lock interface appears, the Java program relies on the Synchronized keyword to implement the lock function, although lock is more explicitly acquired and released when used, but it has the operability of lock acquisition and release, An interruptible acquisition lock and a timeout to acquire the synchronization characteristics of a variety of synchronized keywords that are not available in the lock. Lock's API interface
Lock is an interface, interface, interface. Important thing to say three times. Public
interface Lock {
//Get lock
void Lock ();
interruptible access to lock
void lockinterruptibly () throws interruptedexception;
Try non-blocking fetch lock, immediately return
Boolean trylock ();
Timeout gets
The lock Boolean trylock (long time, Timeunit unit) throws interruptedexception;
Release lock
void Unlock ();
Gets the wait notification component
Condition newcondition ();
}
Internal implementation of lock AQS queue Synchronizer Introduction
The Aqs (abstractqueuedsynchronizer) queue Synchronizer is the underlying framework for building locks or other synchronization components. The synchronization state is generally managed by inheriting the Synchronizer and implementing its abstract methods. The Synchronizer provides 3 methods (GetState (), setstate (int newstate) and compareandsetstate (int expect, int update) to change the synchronization state.
Synchronizer is the key to achieve the lock, the lock in the implementation process of aggregation Synchronizer, the use of Synchronizer to achieve the semantics of the lock.
The Synchronizer provides the following overridable methods:
protected Boolean tryacquire (int var1) {
throw new unsupportedoperationexception ();
}
protected Boolean tryrelease (int var1) {
throw new unsupportedoperationexception ();
}
protected int tryacquireshared (int var1) {
throw new unsupportedoperationexception ();
}
protected Boolean tryreleaseshared (int var1) {
throw new unsupportedoperationexception ();
}
Protected Boolean isheldexclusively () {
throw new unsupportedoperationexception ();
}
AQS Queue Synchronizer Implementation synchronization queue
The Synchronizer relies on internal synchronization queues to complete the synchronization state management, and the queue node data structure is as follows:
Static Final class Node {static final Abstractqueuedsynchronizer.node SHARED = new Abstractqueuedsynchronizer.nod
E ();
Static final Abstractqueuedsynchronizer.node EXCLUSIVE = null;
static final int cancelled = 1;
static final int SIGNAL =-1;
static final int CONDITION =-2;
static final int PROPAGATE =-3;
volatile int waitstatus;
Volatile Abstractqueuedsynchronizer.node prev;
Volatile Abstractqueuedsynchronizer.node next;
volatile thread thread;
Abstractqueuedsynchronizer.node Nextwaiter;
Final Boolean isshared () {return this.nextwaiter = = SHARED; Final Abstractqueuedsynchronizer.node predecessor () throws NullPointerException {Abstractqueuedsyn Chronizer.
Node var1 = This.prev;
if (var1 = = null) {throw new NullPointerException ();
else {return var1; } Node ({} Node (Thread var1, Abstractqueuedsynchronizer.node var2) {this.nextwaiter = var2;
This.thread = var1;
Node (Thread var1, int var2) {this.waitstatus = var2;
This.thread = var1; }
}
From the above code, the synchronous queue is a FIFO bidirectional queue. Exclusive Synchronization State acquisition
Public final void acquire (int var1) {
if!this.tryacquire (var1) && this.acquirequeued (This.addwaiter ( AbstractQueuedSynchronizer.Node.EXCLUSIVE), var1)) {
selfinterrupt ();
}
}
Private node Addwaiter (node mode) {
node node = new Node (Thread.CurrentThread (), mode);
Node pred = tail;
if (pred!= null) {
Node.prev = pred;
if (Compareandsettail (pred, node)) {
pred.next = node;
return node;
}
}
Enq (node);
return node;
}
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;}}}
When the thread calls the acquire (int) method, the specific process is as follows:
Shared Synchronization State Acquisition
Public final void acquireshared (int arg) {
if (tryacquireshared (ARG) < 0)
doacquireshared (ARG);
}
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);
}
Exclusive timeout get sync status
Private Boolean Doacquirenanos (int arg, long nanostimeout) throws Interruptedexception {if (nanostime
Out <= 0L) return false;
Final long deadline = System.nanotime () + nanostimeout;
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 true;
} nanostimeout = Deadline-system.nanotime ();
if (nanostimeout <= 0L) return false;
if (Shouldparkafterfailedacquire (p, node) && nanostimeout > Spinfortimeoutthreshold)
Locksupport.parknanos (this, nanostimeout); if (thread.interrupted ())
throw new Interruptedexception ();
Finally {if (failed) cancelacquire (node); }
}
The specific process is as follows:
Common lock Reentrantlock
Reentrantlock, re-lock, can support a thread to lock the resource repeatedly. This lock supports the fairness and unfairness of the selection when acquiring a lock.
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 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; }
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;
}
The only difference between fair and unfair locks is the hasqueuedpredecessors () method, that is, whether the current node in the synchronization queue has a precursor node.
The fair lock guarantees the acquisition of the lock according to the FIFO principle, while the cost is carried out by a large number of thread switches; The unfair lock may cause the thread to "starve", but the very few threads switch, guaranteed the greater throughput. Reentrantreadwritelock
Reentrantreadwritelock can be read-write lock, the Readwritelock interface is realized, and the Readlock () method and Writelock () method are used to acquire the reading lock and write lock. Read and write State design
Read-write locks need to maintain the state of multiple threads and a write thread in the sync state. The read-write lock divides the variable into two parts, a high 16-bit read, and a low 16-bit representation write. Assuming that the current read-write state is S, the write state equals S&0X0000FFFFF, and the read state equals s>>>16. The write state adds 1 o'clock, equals s+1, reads the state to increase 1 o'clock, equals s+ (1<<16). Write lock Fetch
A write lock is an exclusive lock that supports reentrant entry. If the current thread has acquired a write lock, the write state is added, and the current thread enters the wait state if the read lock has been fetched or the thread that acquired the write lock is not the current thread.
Protected Final Boolean tryacquire (int acquires) {
Thread current = Thread.CurrentThread ();
int c = getState ();
int w = Exclusivecount (c);
if (c!= 0) {
//(Note:if C!= 0 and w = 0 then shared count!= 0)
if (w = = 0 | | current!= GETEXCLUSIVEOWNERTHR EAD ()) return
false;
if (W + exclusivecount (acquires) > Max_count)
throw new Error ("Maximum lock COUNT exceeded");
Reentrant acquire
SetState (c + acquires);
return true;
}
if (Writershouldblock () | |
! Compareandsetstate (c, C + acquires)) return
false;
Setexclusiveownerthread (current);
return true;
}
Read lock Fetch
The
Read lock is a shared lock that supports reentrant, and read locks are always successfully fetched when no other write thread is accessed, and the read status is increased if the current thread has acquired a read lock. When the current thread acquires a read lock, the write lock is acquired by another thread, and it enters the wait state.
Protected Final Boolean tryreleaseshared (int unused) {Thread current = Thread.CurrentThread ();
if (Firstreader = = current) {//Assert firstreaderholdcount > 0;
if (Firstreaderholdcount = = 1) Firstreader = null;
else firstreaderholdcount--;
else {holdcounter RH = Cachedholdcounter;
if (RH = NULL | | | Rh.tid!= getthreadid (current)) RH = Readholds.get ();
int count = Rh.count;
if (count <= 1) {readholds.remove ();
if (count <= 0) throw unmatchedunlockexception ();
}--rh.count; for (;;)
{int c = getState ();
int NEXTC = C-shared_unit; if (Compareandsetstate (c, NEXTC)) return NEXTC = =0; }
}
Lock demotion
Lock demotion is the process of holding the current write lock, acquiring the read lock, and then releasing the write lock. The emphasis is on acquiring the reading lock, keeping the writing lock in the process of acquiring the reading lock, and ensuring the visibility of the data. Condition interface
The condition interface provides an object-like monitor method that can be used in conjunction with lock to implement a wait/notify mode.
Public interface Condition {
//equal to object's Wait () method
void await () throws interruptedexception;
void awaituninterruptibly ();
Long Awaitnanos (long nanostimeout) throws interruptedexception;
A wait (long timeout) method equivalent to object is a
Boolean await (long time, timeunit) throws interruptedexception;
Boolean awaituntil (Date deadline) throws Interruptedexception;
The Notify () method equivalent to object is
void signal ();
The Notifyall () method equivalent to object is
void Signalall ();
}
Summarize
In general, the lock object can replace the Synchronized keyword, which is the advanced level of the Synchronized keyword. Mastering lock helps to learn and contract the principle of the source code, in order to be in the actual development of fluency.