Java并发之AQS浅析

2 篇文章 0 订阅
2 篇文章 0 订阅

0x00 概述

Java并发工具包JUC提供了许多并发工具,如常见的ReentrantLock可重入锁,Semaphore信号量等,它们都有一个共同的父类AbstractQueuedSynchronizer,AQS框架用来构建锁和同步器,该框架底层使用了CAS。

0x01 基本原理

  • AQS类中持有一个volatile的变量state,用来表示同步状态。
  • 图中三个方法用来操作同步状态。

其中,AQS支持两种同步方式,独占式和共享式

  1. 在这里额外说明一点,AbstractQueuedSynchronizer虽然被声明为abstract,但是类中没有任何一个抽象方法,声明为abstract仅仅是为了使得AQS方法无法被实例化,因为上图各个框中的方法默认实现都是直接抛出异常。
  2. 有人可能会异或,为什么要用继承而不用接口实现呢,不是说面向接口编程么,其实是框架的作者站在了开发者的角度来考虑的,如果采用接口的方式,那么开发者在实现自定义同步器的时候,比如要实现一个独占式的同步器,那么他也要实现共享式的方法,这样会给开发者带来疑惑,而采用继承的方式,加上模板方法的使用,就可以在子类中只选择重写需要的方法即可。

0x10 获取同步状态

   public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }

以独占式acquire方法为例。主要步骤如下。

  1. 调用子类复写的tryAcquire方法,如果成功直接返回。否则执行步骤2.
  2. 否则,调用addWaiter方法将新增的EXCLUSIVE同步节点加入同步队列的尾部。
  3. 该节点在同步队列尝试获取同步状态,若获取不到,则阻塞结点线程,直到被前驱结点唤醒或者被中断。
    private Node addWaiter(Node mode) {
        //构建新节点,第一个参数表示nextWaiter,
        Node node = new Node(Thread.currentThread(), mode);
        // Try the fast path of enq; backup to full enq on failure
        Node pred = tail;
        if (pred != null) {
        //尾节点不为空,直接cas添加
            node.prev = pred;
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
        //尾节点为空,进入enq入队列方法
        enq(node);
        return node;
    }

        private Node enq(final Node node) {
        //cas保证正确同步
        for (;;) {
            Node t = tail;
            if (t == null) { // Must initialize
                if (compareAndSetHead(new Node()))
                    tail = head;
            } else {
            //有可能多个线程进入enc方法,所以需要该步骤
                node.prev = t;
                if (compareAndSetTail(t, node)) {
                    t.next = node;
                    return t;
                }
            }
        }

回头看acquireQuequd请求同步队列方法

    final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                final Node p = node.predecessor();//p为node的前驱节点
                if (p == head && tryAcquire(arg)) {//只有p为头节点才进行尝试获取同步方法
                    setHead(node);//获取同步状态成功,设置当前节点为头节点
                    p.next = null; // 显式置为null,GC发生时回收,(因为成功获得了同步状态,所以
                    p已经可被回收了)
                    failed = false;
                    return interrupted;
                    //在整个等待过程中被中断过,则返回true,否则返回false
                }
                //如果p不是头节点,或者没有获取到同步状态
                if (shouldParkAfterFailedAcquire(p, node) &&//判断是否需要阻塞
                    parkAndCheckInterrupt())//进行阻塞
                    interrupted = true;
            }
        } finally {
            if (failed)//如果失败,取消
                cancelAcquire(node);
        }
    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值