AQS队列同步器基本方法(一)

AbstractQueuedSynchronizer 初识队列同步器方法

在了解AbstractQueuedSynchronizer主要方法时,需要先弄清楚Java并发中的独占式和共享式的区别

  1. 不论是独占式还是共享式,都是Java并发包下提供的加锁方式
  2. 独占锁模式:
  • 每次只能有一个线程持有锁
  • 属于悲观保守的加锁策略,避免了读/读冲突,如果某个只读线程获取共享状态,其他所有只读线程只能等待,势必影响性能
  1. 共享锁模式
  • 允许多个线程同时获取锁,因此可以并发访问共享资源
  • 属于乐观锁,放宽了加锁策略,允许多个只读线程同时访问共享资源,也可以被一个写线程访问,但是不能两个写线程同时进行

Node节点

 * @since 1.5
 * @author Doug Lea 有必要了解一下作者
 */ 
   /**
   * Node节点,包括
   * 1、获取同步状态失败的线程引用
   * 2、等待状态
   * 3、前驱节点
   * 4、后继节点
   * 5、节点的属性类型、名称以及描述
   * 6、源码中的数据结构图
   *        <pre>
   *            +------+  prev +-----+       +-----+
   *        head |      | <---- |     | <---- |     |  tail
   *            +------+       +-----+       +-----+
   *        </pre>
   */
    static final class Node {
       //标记该节点在共享模式下等待获取同步状态
        static final Node SHARED = new Node();

       //标记该节点在独占模式下等待获取同步状态
        static final Node EXCLUSIVE = null;

        //处于等待状态的节点被取消
        static final int CANCELLED =  1;

        //后继节点处于处于等待状态,当前节点如果释放了同步状态或者被取消(当前节点状态值为-1)
        //会通知后继节点,使后继节点得以运行
        static final int SIGNAL    = -1;

        /**
        *节点处于等待队列中,节点线程等待在Condition上,其他线程对Condition调用了Signal()
        * 后,该节点会从等待队列中转到到同步队列中,加入到同步队列中以便获取同步状态
        * 这里先解释一下这个吧,听起来很迷糊。该状态主要和源码中的ConditionObject相对应
        * 如果有线程1和2
        * 线程1调用lock方法持有锁
        * 线程1调用await方法进入[条件等待队列],同时释放锁
        * 线程1获取到线程2的signal信号,从[条件等待队列中]进入[同步等待队列]
        * 而对于线程2
        * 在获取锁时,由于线程1持有锁,则进入[同步等待队列]中
        * 1释放锁,2从[同步等待队列]中移除,获取锁,2再调用signal方法,导致1被唤醒
        * 2调用unlock,1获取锁
        *详细参考本类中的ConditionObject和
        *(https://blog.csdn.net/u012420654/article/details/56496631)
        *由此可见,AQS中目前来看,最少维护两个队列,
        * 1、等待获取同步状态的等待队列
        * 2、条件队列,一个节点要么在条件队列中,要么在等待队列中。两者不可同时存在    
        */
        static final int CONDITION = -2;
     
        //下一次的共享状态会被无条件的传播下去
        static final int PROPAGATE = -3;

       
       //等待状态
        volatile int waitStatus;

        //前驱节点
        volatile Node prev;

       //后继节点
        volatile Node next;

       //由类型Thread可知,该参数应该就是节点中获取同步状态的线程了
        volatile Thread thread;

        /**等待节点的后继节点,为什么要这个属性呢?
        * 如果当前等待节点为共享模式或者独占模式,则后继节点的值为SHARED 或者null
        */
        Node nextWaiter;

       
        final boolean isShared() {
            return nextWaiter == SHARED;
        }

        
        final Node predecessor() throws NullPointerException {
            Node p = prev;
            if (p == null)
                throw new NullPointerException();
            else
                return p;
        }

        Node() {    // Used to establish initial head or SHARED marker
        }

        Node(Thread thread, Node mode) {     // Used by addWaiter
            this.nextWaiter = mode;
            this.thread = thread;
        }

        Node(Thread thread, int waitStatus) { // Used by Condition
            this.waitStatus = waitStatus;
            this.thread = thread;
        }
    }```
    
    ```
    //头结点
    private transient volatile Node head;

   //未节点
    private transient volatile Node tail;

   
    private volatile int state;

    //返回同步状态的当前值
    protected final int getState() {
        return state;
    }

    //设置当前同步状态
    protected final void setState(int newState) {
        state = newState;
    }

    //使用CAS设置当前状态,使用CAS可以保证操作的原子性
    protected final boolean compareAndSetState(int expect, int update) {
        // See below for intrinsics setup to support this
        return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
    }

    // Main exported methods

   //独占式获取同步状态,获取成功后,其他线程需要等待该线程释放同步状态(锁)才可以获取
    protected boolean tryAcquire(int arg) {
        throw new UnsupportedOperationException();
    }

    //独占式释放同步状态
    protected boolean tryRelease(int arg) {
        throw new UnsupportedOperationException();
    }

    //共享式获取同步状态,返回值大于0标识获取成功,否则失败
    protected int tryAcquireShared(int arg) {
        throw new UnsupportedOperationException();
    }

    //共享式释放同步状态
    protected boolean tryReleaseShared(int arg) {
        throw new UnsupportedOperationException();
    }

    //当前同步器是否是独占模式下被线程占有(可以理解成同步状态是否被当前线程所占有)
    protected boolean isHeldExclusively() {
        throw new UnsupportedOperationException();
    }

   /**独占模式获取锁,如果当前线程获取同步状态,则返回 
   * 否则,将会进入同步队列进行等待,而该方法又调用了tryAcquire
   */
    public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }

    /**与acquire相同,只是该方法响应中断。
    * 如果当前线程在获取同步状态过程中,进入了同步队列中
    * 如果被中断,则会抛出InterruptedException异常
    */
    public final void acquireInterruptibly(int arg)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        if (!tryAcquire(arg))
            doAcquireInterruptibly(arg);
    }

    /**
    *超时获取同步队列,如果当前线程在nanosTimeout内获取到同步状态
    *返回true,否则返回false
    * 同样,如果因获取同步状态进入了同步队列,如果被中断
    *InterruptedException异常
    */
    public final boolean tryAcquireNanos(int arg, long nanosTimeout)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        return tryAcquire(arg) ||
            doAcquireNanos(arg, nanosTimeout);
    }

   //独占式释放同步状态,在释放同步状态成功后,会唤醒同步队列中第一个节点包含的线程
    public final boolean release(int arg) {
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
        return false;
    }

   /**
   * 共享模式下获取同步状态(锁),如果当前线程获取同步状态失败,则进入同步队列等待
   * 共享式与独占式主要区别在于,同一时刻可以有多个线程持有共享状态
   */
    public final void acquireShared(int arg) {
        if (tryAcquireShared(arg) < 0)
            doAcquireShared(arg);
    }

    //共享式获取同步状态,同时响应中断
    public final void acquireSharedInterruptibly(int arg)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        if (tryAcquireShared(arg) < 0)
            doAcquireSharedInterruptibly(arg);
    }

    //共享模式下获取同步状态,增加超时限制
    public final boolean tryAcquireSharedNanos(int arg, long nanosTimeout)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        return tryAcquireShared(arg) >= 0 ||
            doAcquireSharedNanos(arg, nanosTimeout);
    }

   //共享式释放同步状态
    public final boolean releaseShared(int arg) {
        if (tryReleaseShared(arg)) {
            doReleaseShared();
            return true;
        }
        return false;
    }


   /**
   * 和Node节点中的Condition条件对应,即AQS中维护着这个Condition队列
   /
    public class ConditionObject implements Condition, java.io.Serializable {

    private static final Unsafe unsafe = Unsafe.getUnsafe();
    private static final long stateOffset;
    private static final long headOffset;
    private static final long tailOffset;
    private static final long waitStatusOffset;
    private static final long nextOffset;

    /**
    *在类加载的时候,就加载进方法区中的常量池中,并进行初始化操作
    */
    static {
        try {
            stateOffset = unsafe.objectFieldOffset
                (AbstractQueuedSynchronizer.class.getDeclaredField("state"));
            headOffset = unsafe.objectFieldOffset
                (AbstractQueuedSynchronizer.class.getDeclaredField("head"));
            tailOffset = unsafe.objectFieldOffset
                (AbstractQueuedSynchronizer.class.getDeclaredField("tail"));
            waitStatusOffset = unsafe.objectFieldOffset
                (Node.class.getDeclaredField("waitStatus"));
            nextOffset = unsafe.objectFieldOffset
                (Node.class.getDeclaredField("next"));

        } catch (Exception ex) { throw new Error(ex); }
    }
}
1.看了一天的JVM,有些东西似懂非懂,有些以前迷迷糊糊的东西,也终于明白了,
2.用了两个小时,把目前AQS中自己明白的源码加上注释
3.接下来会对AQS中的实现类源码进行解析并总结出最少一个demo
4.现在的项目中,发现自己的代码被牛人修改了,发现被修改后的代码风格像极了AQS
5.也许在开发项目中,同步状态用不上,但是AQS思想绝对可以借鉴
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,335评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,895评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,766评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,918评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,042评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,169评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,219评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,976评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,393评论 1 304
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,711评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,876评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,562评论 4 336
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,193评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,903评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,142评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,699评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,764评论 2 351

推荐阅读更多精彩内容

  • 一、多线程 说明下线程的状态 java中的线程一共有 5 种状态。 NEW:这种情况指的是,通过 New 关键字创...
    Java旅行者阅读 4,673评论 0 44
  • 对于日本人的评价,似乎有这样的一个共识:有礼貌 。 一百多年前,有人如此描述:“男女皆微笑待人,无言辞激烈,貌失和...
    教官儿阅读 524评论 1 6
  • 今年的夏天 燥热得不一般 整个朋友圈 都透出一股爱情的酸臭味 只有我还保留着 单身狗的清香 随着天气越来越热 身边...
    小小小超玲阅读 481评论 0 1
  • 在和老爸吵架之后,我一直不想见他,也没再联系过他。昨天又和妈妈闹矛盾,我不知道,究竟因为我不够惹人爱,还是因为我...
    蓝鲸儿阅读 771评论 0 2
  • 有些人,你相处了一辈子,却还是彼此陌生。有些人,你只有一面之缘,却让你一见倾心,彼此亲近。 我与危娜的...
    花田林跃丽阅读 494评论 1 9