Analysis of Java lock mechanism AQS

  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.

Guess you like

Origin www.cnblogs.com/gnz49/p/12120156.html
Recommended