AQS(AbstractQueuedSynchronizer)抽象队列同步器

AQS,即AbstractQueuedSynchronizer,是Java并发包中构建锁和其他同步组件的基础框架。本文详细解析AQS的工作原理,包括其内部类Node的属性与方法,以及AQS如何通过维护FIFO双向队列实现线程安全。同时,介绍了AQS的重要方法,如获取和释放独占资源,并展示了基于AQS实现自定义同步器的示例。
摘要由CSDN通过智能技术生成

1.AQS概述

AQS,AbstractQueuedSynchronizer,即队列同步器。它是构建锁或者其他同步组件的基础框架(如ReentrantLock、ReentrantReadWriteLock、Semaphore等),JUC并发包的作者(Doug Lea)期望它能够成为实现大部分同步需求的基础。它是JUC并发包中的核心基础组件,相比synchronized,synchronized缺少了获取锁与释放锁的可操作性,可中断、超时获取锁。

2.Node-AQS内部类

在 AQS内部,其实是通过维护一个FIFO的双向队列来实现线程安全的,双向队列内部元素类型为Node;,如下图所示,为Node内部类相关的方法以及属性,下面对相关方法和属性做简要分析
在这里插入图片描述
1. SHARED,EXCLUSIVE
       表示线程是因什么被挂起,从而放入aqs等待队列中去的,shared表示获取共享资源被放到aqs队列中,而exclusive是因为获取独占资源被放入等待队列中去。

2.waitStatus
       表示线程等待状态,值的改变是通过cas算法修改的。

3.CANCELLED,SIGNAL,CONDITION ,PROPAGATE
       表示线程状态取值,各个值的含义如下:

  • CANCELLED:线程被取消,一般是因为超时或者被打断,此种类型的线程,是永远不会被 阻塞的。
  • SIGNAL:线程需要被唤醒,一般是通过调用acquire让线程的标志为该状态,如果获取失败,则线程被阻塞。
  • CONDITION:线程在同步队列中。
  • PROPAGATE: 释放共享资源时需要通知其他节点

4.prev
       记录当前节点的前驱节点。
5.next
       记录当前节点的后继节点 。

3.AQS内部重要方法

       在 AQS 中 维持了 一 个 单 一 的状态信息 state,可以通过 getState 、 setState 、compareAndS etState 函数修改其值了;锁的释放和获取其实都是围绕state值的变化。下面为aqs在获取独占资源的时候,获取锁的步骤模板。需要注意的是 , AQS 类并没有提供可用的TryAcquire 和TryRelease 方法,对于state表示的意思,也是由子类自己定义的; 如Reentran tLock 的 实 现来说, state 可以用 来表示当前线程锁的重入次数,对于 读写锁 ReentrantReadWriteLock 来说 ,state 的 高16位表示读状态,也就是获取该读锁的次数,低 16 位表示获取到写锁的线程的可重入次数

1.获取独占资源

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

       当一个线程调用acquire获取资源的时候,首先会调用tryAcquire方法尝试获取资源,具体的操作是设置state变量的值,成功则返回,失败则将该线程封装为Node.EXCLUSIVE的节点到aqs队列中去(入队列方法请继续看下面),并调用LockSupport.park(this) 方法挂起自己 。

2.释放独占资源

public final boolean release(int arg) {
    if (tryRelease(arg)) {
        Node h = head;
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h);
        return true;
    }
    return false;
}

       当 一个线程调用 re le ase( int arg)方法时会尝试使用 tryRele ase 操作释放资源,这里是设置状态变量 state 的值,然后调用 LockSupport.unpark(thread)方法激活 AQS 队列里面被阻塞的一个线程(thread) 。 被激活的线程则使用 tryAcquire 尝试,看当前状态变量 state的值是否能满足自己的需要,满足则该线程被激活,然后继续 向下运行,否则还是会被放入 AQS队列并被挂起。

3.入队操作

 private Node addWaiter(Node mode) {
        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;
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
        enq(node);
        return node;
    }
private Node enq(final Node node) {
        for (;;) {
            Node t = tail;
            //1:
            if (t == null) { // Must initialize
                if (compareAndSetHead(new Node()))
                    tail = head;
            } else {
            //2:
                node.prev = t;
                if (compareAndSetTail(t, node)) {
                    t.next = node;
                    return t;
                }
            }
        }
    }

       在上面例子说过,在获取资源的时候, 当 一个线程获取锁失败后该线程会被转换为 No de 节点,然后就会使用enq(final Node node) 方法将该节点插入到 AQS 的阻塞队列。下面结合图和代码来讲解入队操作
在这里插入图片描述
                                                                                           图I
       初始状态下,tail和head都为null,代码会执行1处,tail和head会指向同一个节点。队列状态如图(I)所示
在这里插入图片描述
                                                                                    图II
       当执行2处循环的时候,会通过cas算法判断是否能够正确获取到尾部节点,如果能够正确获取到的话,会按照图II所示步骤进行入队操作,如果不能正确获取到尾部节点,则再次执行循环体,知道能够正确获取到tail的值。

4.基于 AQS 实现自定义同步器

       如下代码是基于 AQS 实现的独 占锁,对于原子变量 state 的含义,state 为 0 表示目前锁没有被线程持有 state 为 l 表示锁己经被某一个线程持有.

public class CustomerNonReentrantLock implements Lock {
    /**
     * 创建sync来做具体工作
     */
    private final Sync sync = new Sync();

    @Override
    public void lock() {
        sync.acquire(1);
    }

    @Override
    public void lockInterruptibly() throws InterruptedException {
        sync.acquireInterruptibly(1);
    }

    @Override
    public boolean tryLock() {
        return sync.tryAcquire(1);
    }

    @Override
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
        return sync.tryAcquireNanos(1, unit.toNanos(time));
    }

    @Override
    public void unlock() {
        sync.release(1);
    }

    @Override
    public Condition newCondition() {
        return sync.newCondition();
    }

    /**
     * 内部帮助类
     */
    private static class Sync extends AbstractQueuedSynchronizer {
        /**
         * 锁是否被持有
         *
         * @return true:
         */
        @Override
        protected boolean isHeldExclusively() {
            return getState() == 1;
        }

        @Override
        protected boolean tryAcquire(int arg) {
            assert arg == 1;
            if (compareAndSetState(0, 1)) {
                setExclusiveOwnerThread(Thread.currentThread());
                setState(1);
                return true;
            }
            return false;
        }

        /**
         * 释放锁
         *
         * @param arg 状态值
         * @return true:成功
         */
        @Override
        protected boolean tryRelease(int arg) {
            assert arg == 1;
            if (getState() == 0) {
                throw new IllegalMonitorStateException();
            }
            setExclusiveOwnerThread(null);
            setState(0);
            return true;
        }

        Condition newCondition() {
            return new ConditionObject();
        }

    }


    final static CustomerNonReentrantLock lock = new CustomerNonReentrantLock();
    final static Condition notFull = lock.newCondition();
    final static Condition notEmpty = lock.newCondition();
    final static Queue<String> queue = new LinkedBlockingQueue<String>();
    final static int queueSize = 10;

    /**
     * 观察者消费者模式测试
     *
     * @param args args
     */
    public static void main(String[] args) throws InterruptedException {
        Thread produce = new Thread(new Runnable() {
            @Override
            public void run() {
                lock.lock();
                try {
                    //队列满,等待
                    while (queue.size() == queueSize) {
                        notEmpty.await();
                    }
                    //添加队列元素
                    queue.add("ele");
                    System.out.println("生产者生产完成.");
                    //唤醒消费线程
                    notFull.signalAll();
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }

            }
        });

        Thread consumer = new Thread(new Runnable() {
            @Override
            public void run() {
                lock.lock();
                System.out.println("消费者获取锁");
                try {
                    //队列为空,停止消费
                    while (queue.size() == 0) {
                        System.out.println("队列为空");
                        notFull.await();
                    }
                    //
                    String ele = queue.poll();
                    System.out.println("消费者消费完成.");
                    //唤醒生产线程
                    notEmpty.signalAll();

                } catch (Exception e) {
                    e.printStackTrace();
                    ;
                } finally {
                    lock.unlock();
                }
            }
        });

        produce.start();
        //Thread.sleep(20);
        consumer.start();
    }
}

       此处为博主个人总结,可能能力有限,总结不当的,可留下评论,一起探讨。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值