Java中的AQS基本原理

AQS即AbstractQueuedSynchronizer缩写,翻译为抽象队列同步器,平时使用较多的ReentrantLock、CountDownLatch就是基于AQS实现。

AQS只是一个框架,具体的资源获取/释放得由自定义的同步器去实现,同步器的设计是基于模板方法模式因此AQS类中提供了一个重要的成员变量 state,用来给自定义同步器保存当前同步状态,并提供了对应的get/set方法来访问或修改同步状态。

    /**
     * 同步状态
     */
    private volatile int state;

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

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

    /**
     * 如果当前状态值等于预期值,则原子地将同步状态设置为给定的更新值,CAS操作。
     */
    protected final boolean compareAndSetState(int expect, int update) {
        return U.compareAndSwapInt(this, STATE, expect, update);
    }

资源的获取与释放在AQS中分为独占式与共享式,分别提供了如下方法:

方法名描述
acquare()独占模式获取同步状态,忽略中断。如果当前线程获取同步状态成功则返回。否则线程会排队,可能会反复阻塞和解除阻塞,调用 tryAcquire 直到成功
acquareInterruptibly()独占模式获取,如果中断则中止。首先检查中断状态来实现,如果当前线程获取同步状态成功返回。否则线程会排队,可能会重复阻塞和解除阻塞,调用tryAcquire直到成功或线程被中断
tryAcquireNanos()独占模式获取,如果中断则中止。在acquareInterruptibly基础上增加了超时限制
acquireShared()共享模式下获取同步状态,忽略中断。如果当前线程未获取到同步状态,将会进入同步队列等待。与独占锁主要区别是同一时刻有多个线程获取到同步状态
acquireSharedInterruptibly()共享模式下获取,如果中断则中止。其他与acquireShared相同
tryAcquireSharedNanos()共享模式下获取,如果被中断则中止。在acquireSharedInterruptibly基础上增加了超时限制

AQS内部使用了CLH队列,是CLH队列锁的一种变体实现。CLH队列锁即Craig、Landin、Hagersten Locks,三个人名字。CLH队列锁是一种基于链表的自旋锁。

          +---------+  prev +---------+       +---------+
     head | P  |  L | <---- | P  |  L | <---- | P  |  L |  tail
          +---------+       +---------+       +---------+
               A                 B                 C

只有队列首个线程A能取到锁,后续线程以链表形式排列,其中有一个P(myPred)节点用来指向前一个元素,一个L(locked)节点表示当前线程是否需要获取锁。当B线程需要获得锁,就在A线程的locded字段上做旋转,直到A释放锁(即前一个locked=false),这时B就可以获取到锁,同时回收前驱结点A。

CountDownLatch使用

假设有A、B、C三个线程,其中A、B线程执行分为三步,C线程需要在A、B都执行到第二步才能执行,怎么实现?

        final CountDownLatch countDownLatch =new CountDownLatch(2);
        Thread thread_A =  new Thread(new Runnable() {
            @Override
            public void run() {
                Log.d(TAG,"thread_A第一步执行");
                Log.d(TAG,"thread_A第二步执行");
                countDownLatch.countDown();
                Log.d(TAG,"thread_A第三步执行");
            }
        });
        Thread thread_B =  new Thread(new Runnable() {
            @Override
            public void run() {
                Log.d(TAG,"thread_B第一步执行");
                Log.d(TAG,"thread_B第二步执行");
                countDownLatch.countDown();
                Log.d(TAG,"thread_B第三步执行");
            }
        });
        Thread thread_C =  new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    //等待线程A、B执行完毕
                    countDownLatch.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                Log.d(TAG,"thread_C执行");
            }
        });
        thread_C.start();
        thread_B.start();
        thread_A.start();

当调用await方法,若状态值为0则不会发生阻塞,否则发生阻塞。调用countDown方法后,会利用CAS机制将状态值-1,直到状态值为0时,await将不再阻塞。 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

永琪-

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值