Java并发-AbstractQueuedSynchronizer并发框架

AQS(AbstractQueuedSynchronizer)

AQS是一个同步框架,框架具有的特性
1、通用性,下层实现透明的同步机制,同时与上层业务解耦
2、利用CAS,原子的修改共享标记位
3、等待队列:两种业务场景,1、线程只想尝试获取锁,如果没有获取到就干其他的事情,2、有的业务一定要获取到共享资源才能进行下一步处理,如果当前时刻没有获取到锁它愿意等待。
第二种情况就设计一个等待队列。

线程获取锁的两种方式,独占和共享
独占模式:一旦获取锁,其他线程不能占用
共享模式:其他线程在共享的模式下可以继续占用

1、用于共享变量是否被占用的标记位
为什么state是int类型,因为在共享模式下可能有多个线程占用资源。因此state表示线程占用的数量
volatile:保证了可见性
2、等待队列FIFO双向队列

	//头节点
    private transient volatile Node head;
    //尾节点
    private transient volatile Node tail;
    /**
     * The synchronization state.
     */
    private volatile int state;

tryacqure()方法

该方法只有一行实现,就是抛出一个异常
需要继承类重写这个方法。
给上层调用开放了空间,可以根据自己的业务编写逻辑

    protected boolean tryAcquire(int arg) {
        throw new UnsupportedOperationException();
    }

acqure()方法

final不允许修改

    public final void acquire(int arg) {
    /**当线程一来想要获取锁,就尝试去获取锁,如果获取锁失败就加入等待队列获取锁
    在这里体现了aqs是一个非公平锁*/
    //如果tryAcquire()获取锁,就跳出代码快
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }

addWaiter()方法

当尝试获取锁失败后,把当前线程创建成一个node节点放在队列尾部(尝试入队)
enq并进入完整的入队方法。

    private Node addWaiter(Node mode) {
    //创建一个node节点
        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) {
            node.prev = pred;
            /**尝试通过cas的方式入队*/
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
        enq(node);
        return node;
    }

enq()完整入队方法

    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;
                }
            }
        }
    }

acquireQueued()

队列中,
1、如果该节点处于头节点后面一个才会CAS去拿锁直到拿锁成功,
2、其他节点会被挂起,提升程序性能。
什么时候线程才会被唤醒?请看release方法

    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;
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

unparkSuccessor()唤醒挂起线程

当一个线程拿到锁并且执行完逻辑之后,会唤醒等待队列中的线程,去唤醒的时候倒序搜索队列,
拿锁,挂起,释放锁,唤醒形成一个良好的工作闭环。

    private void unparkSuccessor(Node node) {
   /**node节点就是拿到锁的线程,并且成功执行完逻辑*/
        int ws = node.waitStatus;
        //通过cas将状态改为默认值
        if (ws < 0)
            compareAndSetWaitStatus(node, ws, 0);
            //将node节点的下一个节点赋值给s
        Node s = node.next;
        /**如果s节点为空,并且等待状态为默认值*/
        if (s == null || s.waitStatus > 0) {
            s = null;
            /**从队尾开始搜索*/
            for (Node t = tail; t != null && t != node; t = t.prev)
                if (t.waitStatus <= 0)
                /**找到等待状态小于等于0的节点,赋值给s*/
                    s = t;
        }
        /**如果s节点不为空则唤醒s节点,叫他起来干活*/
        if (s != null)
            LockSupport.unpark(s.thread);
    }

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值