• Java多线程--AQS


    ReentrantLock和AQS的关系

    首先我们来看看,如果用java并发包下的ReentrantLock来加锁和释放锁,是个什么样的:

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

     上面那段代码就是搞一个Lock对象,然后加锁和释放锁。那么,这个跟AQS有啥关系?关系大了去了,因为java并发包下很多API都是基于AQS来实现的加锁和释放锁等功能的,AQS是java并发包的基础类。上一部分源码:

     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 }

    说白了,ReentrantLock内部包含了一个AQS对象,也就是AbstractQueuedSynchronizer类型的对象。这个AQS对象就是ReentrantLock可以实现加锁和释放锁的关键性的核心组件。

    ReentrantLock加锁和释放锁的底层原理

    现在如果有一个线程过来尝试用ReentrantLock的lock()方法进行加锁,会发生什么事情呢?

     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 }

    这个AQS对象内部有一个核心的变量叫做state,是int类型的,代表了加锁的状态。初始状态下,这个state的值是0。另外,这个AQS内部还有一个关键变量,用来记录当前加锁的是哪个线程,初始化状态下,这个变量是null。接着线程1跑过来调用ReentrantLock的lock()方法尝试进行加锁,这个加锁的过程,直接就是用CAS操作将state值从0变为1。如果之前没人加过锁,那么state的值肯定是0,此时线程1就可以加锁成功。一旦线程1加锁成功了之后,就可以设置当前加锁线程是自己。

    AQS就是并发包里的一个核心组件,里面有state变量、加锁线程变量等核心的东西,维护了加锁状态。ReentrantLock这种东西只是一个外层的API,内核中的锁机制实现都是依赖AQS组件的。这个ReentrantLock之所以用Reentrant打头,意思就是他是一个可重入锁。意思就是你可以对一个ReentrantLock对象多次执行lock()加锁和unlock()释放锁,也就是可以对一个锁加多次,叫做可重入加锁。大家看明白了那个state变量之后,就知道了如何进行可重入加锁!其实每次线程1可重入加锁一次,会判断一下当前加锁线程就是自己,那么他自己就可以可重入多次加锁,每次加锁就是把state的值给累加1,别的没啥变化,实现原理如下:

     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 }

    接着,如果线程1加锁了之后,线程2跑过来加锁会怎么样呢?我们来看看锁的互斥是如何实现的,线程2跑过来一下看到state的值不是0,所以CAS操作将state从0变为1的过程会失败,因为state的值当前为1,说明已经有人加锁了!接着线程2会看一下,是不是自己之前加的锁啊?当然不是了,“加锁线程”这个变量明确记录了是线程1占用了这个锁,所以线程2此时就是加锁失败。接着,线程2会将自己放入AQS中的一个等待队列,因为自己尝试加锁失败了,此时就要将自己放入队列中来等待,等待线程1释放锁之后,自己就可以重新尝试加锁了,所以大家可以看到,AQS是如此的核心。AQS内部还有一个等待队列,专门放那些加锁失败的线程。

     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 }

    接着,线程1在执行完自己的业务逻辑代码之后,就会释放锁,他释放锁的过程非常的简单,就是将AQS内的state变量的值递减1,如果state值为0,则彻底释放锁,会将“加锁线程”变量也设置为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 }

    接下来,会从等待队列的队头唤醒线程2重新尝试加锁。好!线程2现在就重新尝试加锁,这时还是用CAS操作将state从0变为1,此时就会成功,成功之后代表加锁成功,就会将state设置为1。此外,还要把“加锁线程”设置为线程2自己,同时线程2自己就从等待队列中出队了。

    其实一句话总结:AQS就是一个并发包的基础组件,用来实现各种锁,各种同步组件的。它包含了state变量、加锁线程、等待队列等并发中的核心组件。

  • 相关阅读:
    Excel宏打开URL 特殊字符#变了
    windows升级到1607后操作很卡顿的解决办法
    打包解决方案后,安装时提示只能在IIS5.1以上运行解决方法
    在CS代码页获取input输入框内肉----.net学习点滴
    greenplum 6.7编译安装插件pxf
    greenplum 6.7编译安装插件mysql_fdw
    python3 + greenplum-spark-connector访问greenplum
    centos7无cm安装hadoop+spark
    hive实现update和delete功能
    使得hivecli访问hive
  • 原文地址:https://www.cnblogs.com/ding-dang/p/11072290.html
Copyright © 2020-2023  润新知