• What is AbstractQueuedSynchronizer

Is used toBuild the lockOr otherSynchronizer componentThe heavyweight infrastructure framework and the cornerstone of the entire JUC system, through the built-inFIFO queueTo complete the queuing of the resource acquisition thread, using int type variable to represent the status of the lock. (Abstract queue synchronizer)

  • Who is associated with AbstractQueuedSynchronizer

CountDownLatch

ReentrantLock

Semaphore

This is just a small sample.

  • CLH queue

  • What’s the use of AbstractQueuedSynchronizer

If shared resources are occupied, some blocking wake-up mechanism is required to ensure lock allocation. This mechanism is mainly implemented with CLH queue, which adds threads that cannot acquire locks temporarily to queue, which is an abstract representation of AQS. It encapsulates the thread requesting shared resources into the Node of the queue, and maintains the state of the state variable through CAS, spin and locksupport.park (), so as to achieve the synchronous effect of concurrency.

AQS uses a volatile int member variable to represent the synchronization state, completes the queuing of resource acquisition through the built-in FIFO queue, encapsulates the thread to seize resources into a Node Node to realize the allocation of locks, and completes the modification of state value through CAS.

  • The Node of AbstractQueuedSynchronizer (JDK1.8)

Shared: Threads wait for locks in shared mode.

Exclusive: Indicates that a thread is waiting for a lock exclusively.

Cancelled: 1 indicates that the thread’s lock acquisition request has been cancelled.

Signal: -1: indicates that the thread is ready for resource release.

Condition: -2, indicating that the node is in the wait queue and the node thread is waiting to wake up.

Propagate: is -3, the field is only used when the current thread is in shared.

WaitStatus: indicates the status of the current node in the queue.

Prev: indicates the precursor pointer.

Next: the successor pointer.

Thread: indicates the thread of the current node.

NextWaiter: points to the next node in condition.

  • AbstractQueuedSynchronizer source (JDK1.8)

AQS= State +CLH queue, Node=waitStatus+Thread

The following uses ReentrantLock as an example.

public class ReentrantLockDemo { public static void main(String[] args) { ReentrantLock reentrantLock = new ReentrantLock(); // Thread A new Thread(()->{reentrantlock. lock(); try { System.out.println("A"); try { TimeUnit.MILLISECONDS.sleep(50); }catch (Exception e){ e.printStackTrace(); } }finally { reentrantLock.unlock(); } },"A").start(); // Thread B new Thread(()->{reentrantlock. lock(); try { System.out.println("B"); try { TimeUnit.MILLISECONDS.sleep(5); }catch (Exception e){ e.printStackTrace(); } }finally { reentrantLock.unlock(); } },"B").start(); Thread C new Thread(()->{reentrantlock. lock(); try { System.out.println("C"); }finally { reentrantLock.unlock(); } },"C").start(); }}Copy the code

reentrantLock.lock()

Reentrantlock. lock() calls lock() in Sync class

Lock () is a template mode, and the NonfairSync and FairSync classes implement lock(), respectively.

In the case of NonfairSync, the first step is to use CAS to determine whether state=0 can be updated to state=1.

Set exclusiveOwnerThread to the current thread if you have the lock, otherwise enter Acquire (int arg).

Enter tryAcquire(ARG), tryAcquire(int ARg) is the template mode, defined in NonfairSync as shown below.

When a thread cannot obtain a lock, addWrite(Node.exclusive) enters enq(Node) because preD is NULL.

Enq (node) to use for (;;) CAS initializes the node.

Return the Node Node to acquireQueued(addWaiter(Node.exclusive), arg).

Enter shouldParkAfterFailedAcquire (p, node) will of the sentinel node waitStatus updates to 1, return false.

For the second time in shouldParkAfterFailedAcquire (p, node) returns true, enter parkAndCheckInterrupt (), B thread hung.

Thread C calls addWrite(Node.exclusive) to initialize the Node as thread B does.

Update B’s waitStatus to -1, continue spinning, and suspend thread C.

reentrantLock.unlock()

Call the sync. Release (1).

Enter tryRelease(ARG) and update state and exclusiveOwnerThread to NULL.

Returns true to enter the unparksucceeded (h), freeing the locks held by thread A.

At this point, thread B enters the red box (at this point, thread D can grab the lock, causing B to continue spinning). If B grabs the lock, the reference of the precursor node is set to NULL for GC.

C thread and so on.