Java multi-threaded --AQS

AQS and the relationship ReentrantLock

First we look at, and if java contract under ReentrantLock to lock and release the lock, what kind of:

1 ReentrantLock reentrantLock = new ReentrantLock();
2 reentrantLock.lock();
3 //业务代码
4 reentrantLock.unlock();

 The above part of the code is to engage in a Lock object, and then lock and release the lock. So, what's this relationship with AQS? Relations go big, because in a lot of java and API contract are based AQS to achieve locking and releasing locks and other functions, AQS is the base class of java and contract. A portion of the source code:

 1 public class ReentrantLock implements Lock, java.io.Serializable {
 2     private static final long serialVersionUID = 7373984872572414699L;
 3     /** Synchronizer providing all implementation mechanics */
 4     private final Sync sync;
 5 
 6     /**
 7      * Base of synchronization control for this lock. Subclassed
 8      * into fair and nonfair versions below. Uses AQS state to
 9      * represent the number of holds on the lock.
10      */
11     abstract static class Sync extends AbstractQueuedSynchronizer {
12         private static final long serialVersionUID = -5179523762034025860L;
13 
14         /**
15          * Performs {@link Lock#lock}. The main reason for subclassing
16          * is to allow fast path for nonfair version.
17          */
18         abstract void lock();
19 
20         /**
21          * Performs non-fair tryLock.  tryAcquire is implemented in
22          * subclasses, but both need nonfair try for trylock method.
23          */
24         final boolean nonfairTryAcquire(int acquires) {
25             final Thread current = Thread.currentThread();
26             int c = getState();
27             if (c == 0) {
28                 if (compareAndSetState(0, acquires)) {
29                     setExclusiveOwnerThread(current);
30                     return true;
31                 }
32             }
33             else if (current == getExclusiveOwnerThread()) {
34                 int nextc = c + acquires;
35                 if (nextc < 0) // overflow
36                     throw new Error("Maximum lock count exceeded");
37                 setState(nextc);
38                 return true;
39             }
40             return false;
41         }
42 
43         protected final boolean tryRelease(int releases) {
44             int c = getState() - releases;
45             if (Thread.currentThread() != getExclusiveOwnerThread())
46                 throw new IllegalMonitorStateException();
47             boolean free = false;
48             if (c == 0) {
49                 free = true;
50                 setExclusiveOwnerThread(null);
51             }
52             setState(c);
53             return free;
54         }
55 
56         protected final boolean isHeldExclusively() {
57             // While we must in general read state before owner,
58             // we don't need to do so to check if current thread is owner
59             return getExclusiveOwnerThread() == Thread.currentThread();
60         }
61 
62         final ConditionObject newCondition() {
63             return new ConditionObject();
64         }
65 
66         // Methods relayed from outer class
67 
68         final Thread getOwner() {
69             return getState() == 0 ? null : getExclusiveOwnerThread();
70         }
71 
72         final int getHoldCount() {
73             return isHeldExclusively() ? getState() : 0;
74         }
75 
76         final boolean isLocked() {
77             return getState() != 0;
78         }
79 
80         /**
81          * Reconstitutes the instance from a stream (that is, deserializes it).
82          */
83         private void readObject(java.io.ObjectInputStream s)
84             throws java.io.IOException, ClassNotFoundException {
85             s.defaultReadObject();
86             setState(0); // reset to unlocked state
87         }
88     }
89 }

To put it plainly, the interior contains a ReentrantLock AQS object is an object AbstractQueuedSynchronizer type. This object is the AQS ReentrantLock can achieve a critical core component of the lock and release the lock.

The underlying principle ReentrantLock lock and release the lock

Now if there is a thread over attempts by locking methods lock ReentrantLock of (), what will happen then?

 1 public abstract class AbstractQueuedSynchronizer
 2     extends AbstractOwnableSynchronizer
 3     implements java.io.Serializable {
 4 
 5    /**
 6     * The thread that enqueued this node.  Initialized on
 7     * construction and nulled out after use.
 8     */
 9     volatile Thread thread;
10 
11     /**
12      * The synchronization state.
13      */
14     private volatile int state;
15 
16 }

The AQS internal object has a variable called core state, is of type int represents the locked state. In the initial state, the state value is 0. In addition, the AQS inside there is a key variable for the current record which thread is locked, initialized state, this variable is null. Thread 1 then calls the lock ReentrantLock ran () method attempts to lock, the locking process, is to use directly the CAS operation state value from 0 to 1. If you do not add people had before the lock, then the value of state is certainly 0, then the thread 1 can be locked successfully. Once locked thread 1 is successful, you can set the current thread is locked themselves.

AQS is a core component of concurrent bag, there are state variables, locking the core of things such as thread variables, maintaining the locked state. This is just a thing ReentrantLock outer layer API, the kernel locking mechanisms implemented are dependent on the AQS assembly. The reason to use Reentrant ReentrantLock beginning, meaning that he is a reentrant lock. Meaning that you can lock multiple times for a ReentrantLock Object () lock and unlock () to release the lock, that can be added to many times, called reentrant lock for a lock. We understand that after the state variables, you know how to reentrant lock! In fact, every thread 1 reentrant lock once, will determine what the current lock thread is their own, so he can re-entrant lock several times, each time the lock is to state the value of 1 to accumulate, other nothing changes, implement the following principles:

 1 public class ReentrantLock implements Lock, java.io.Serializable {
 2     /**
 3      * Sync object for non-fair locks
 4      */
 5     static final class NonfairSync extends Sync {
 6         private static final long serialVersionUID = 7316153563782823691L;
 7 
 8         /**
 9          * Performs lock.  Try immediate barge, backing up to normal
10          * acquire on failure.
11          */
12         final void lock() {
13             if (compareAndSetState(0, 1))
14                 setExclusiveOwnerThread(Thread.currentThread());
15             else
16                 acquire(1);
17         }
18 
19         protected final boolean tryAcquire(int acquires) {
20             return nonfairTryAcquire(acquires);
21         }
22     }
23 }
24 
25 public abstract class AbstractQueuedSynchronizer
26     extends AbstractOwnableSynchronizer
27     implements java.io.Serializable {
28 
29     /**
30      * Acquires in exclusive mode, ignoring interrupts.  Implemented
31      * by invoking at least once {@link #tryAcquire},
32      * returning on success.  Otherwise the thread is queued, possibly
33      * repeatedly blocking and unblocking, invoking {@link
34      * #tryAcquire} until success.  This method can be used
35      * to implement method {@link Lock#lock}.
36      *
37      * @param arg the acquire argument.  This value is conveyed to
38      *        {@link #tryAcquire} but is otherwise uninterpreted and
39      *        can represent anything you like.
40      */
41     public final void acquire(int arg) {
42         if (!tryAcquire(arg) &&
43             acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
44             selfInterrupt();
45     }
46 
47 }

Then, after locking up if the thread 1, thread 2 lock ran what will happen? Let's see how the mutex lock is achieved, thread 2 ran to see what the value is not 0 state, so the CAS operation state from 0 to 1, the process will fail, because the value of the current state of 1, indicating It has been locked up! Thread 2 will then look at, it is not to add their own locks before ah? Of course not, "locking threads" This variable clear record of thread-1 takes up the lock, so the thread 2 at that point lock failure. Then, after the thread 2 will put yourself in a queue AQS, because they try to lock fails, then it must be placed in its own queue waiting, waiting threads 1 to release the lock, and that they can try to re-add lock, so you can see, AQS is so central. AQS inside there is a waiting queue, specially put those threads lock failure.

 1 /**
 2  * Condition implementation for a {@link
 3  * AbstractQueuedSynchronizer} serving as the basis of a {@link
 4  * Lock} implementation.
 5  *
 6  * <p>Method documentation for this class describes mechanics,
 7  * not behavioral specifications from the point of view of Lock
 8  * and Condition users. Exported versions of this class will in
 9  * general need to be accompanied by documentation describing
10  * condition semantics that rely on those of the associated
11  * {@code AbstractQueuedSynchronizer}.
12  *
13  * <p>This class is Serializable, but all fields are transient,
14  * so deserialized conditions have no waiters.
15  */
16 public class ConditionObject implements Condition, java.io.Serializable {
17         private static final long serialVersionUID = 1173984872572414699L;
18         /** First node of condition queue. */
19         private transient Node firstWaiter;
20         /** Last node of condition queue. */
21         private transient Node lastWaiter;
22 
23         /**
24          * Creates a new {@code ConditionObject} instance.
25          */
26         public ConditionObject() { }
27 
28         // Internal methods
29 
30         /**
31          * Adds a new waiter to wait queue.
32          * @return its new wait node
33          */
34         private Node addConditionWaiter() {
35             Node t = lastWaiter;
36             // If lastWaiter is cancelled, clean out.
37             if (t != null && t.waitStatus != Node.CONDITION) {
38                 unlinkCancelledWaiters();
39                 t = lastWaiter;
40             }
41             Node node = new Node(Thread.currentThread(), Node.CONDITION);
42             if (t == null)
43                 firstWaiter = node;
44             else
45                 t.nextWaiter = node;
46             lastWaiter = node;
47             return node;
48         }
49 }

Next, thread 1 after the completion of the implementation of their business logic code will release the lock, he releases the lock process is very simple, is decremented by one state variable value in the AQS, if the state value is 0, then completely release lock , will be "locking threads" variable is also set to null!

 1 public class ReentrantLock implements Lock, java.io.Serializable {  
 2     /**
 3      * Attempts to release this lock.
 4      *
 5      * <p>If the current thread is the holder of this lock then the hold
 6      * count is decremented.  If the hold count is now zero then the lock
 7      * is released.  If the current thread is not the holder of this
 8      * lock then {@link IllegalMonitorStateException} is thrown.
 9      *
10      * @throws IllegalMonitorStateException if the current thread does not
11      *         hold this lock
12      */
13     public void unlock() {
14         sync.release(1);
15     }
16 }
17 
18 public abstract class AbstractQueuedSynchronizer
19     extends AbstractOwnableSynchronizer
20     implements java.io.Serializable {
21      public final boolean release(int arg) {
22         if (tryRelease(arg)) {
23             Node h = head;
24             if (h != null && h.waitStatus != 0)
25                 unparkSuccessor(h);
26             return true;
27         }
28         return false;
29     }
30 }

 

Next, the team will wake up from the head of the waiting queue thread 2 attempts to re-lock. it is good! Thread 2 will now try to re-lock, then still with the CAS operation state from 0 to 1, will succeed this time, after successfully represented a lock is successful, the state is set to 1. Also, the "locking threads" Thread 2 is set to own, while thread 2 himself out of the team from the waiting queue.

In fact, one sentence summary: basic components and AQS is a contract, and to achieve a variety of locks, various synchronization components. It contains state variables, locking thread, waiting for the core components of concurrent queues.

Guess you like

Origin www.cnblogs.com/ding-dang/p/11072290.html