zoukankan      html  css  js  c++  java
  • AQS,ReentrantLock和ConditionObject

    介绍一下AQS:

    注意看绿色的方法,这些是对外开放的方法。

    另外,AQS是一个CLH变种双边队列,原始的CLH是.net人员设计的,他们设计的时候是让每个阻塞在队列上的线程不停的自旋,而在java中借鉴了这种设计方式,但是不再是自旋,而是通过LockSupport.park(this);这样一个方法将线程阻塞。

    ***********************

    acquire方法:

      其中acquire开头的方法是AQS默认实现的通用加锁方法,带有Interruptibly结尾的是处理打断状态的请求方法,不带的是不处理打断标志的方法。带有shared的是共享锁的加锁方法,不带有的则是独占锁的加锁方法。

     上面是AQS中的acquire方法(AQS实现的这个是公平锁的获取方式):独占锁获取方法,先调用tryAcquire方法,如果成功则加锁成功,如果失败则线程开始排队,并阻塞自己。tryAcquire方法如下:

    方法的注释解释的很清楚,子类应该去实现这个方法,因为此方法会被acquire方法调用,而独占锁获取锁必须调用acquire方法,如果你实现的是一个独占锁,那么你必须实现此方法,如果是共享锁则不必。

    实现此方法时:当返回失败,则此线程应该排队(直到被另外一个线程通过release唤醒或者它被中断),如果成功则上锁成功。

    ******************

    release方法:

      release方法是AQS释放阻塞线程的方法,让其去竞争锁,带有shared是共享锁的释放方法,不带的则是独占锁的方法。思路一致。这里看下公平锁的release方

     注释很清楚,AQS负责释放阻塞线程,子类实现自己的tryRelease方法去释放锁。其他的方法就不赘述了。

    *************************

    介绍下ReentrantLock:

      ReentrantLock是实现AQS的独占锁,它内部先是定义了一个抽象同步器Sync实现了AQS,这个方法定义了非公平锁尝试获取锁的方式(尝试获取锁是不会阻塞的,只会返回成功或失败)。

    然后是公平锁和非公平锁:

       结合之前AQS的介绍,相信我们一眼就可以看透这两个类了,非公平锁的lock方法是自己利用CAS实现的抢占式加锁,如果失败则专用AQS的公平锁加锁方式;而公平锁就简单多了lock方法调用的就是AQS提供的加锁方式。

      而尝试获取锁的方式都是自定义的,前者定义在sync中后者定义在公平锁子类中。代码不复杂,不需要赘述了。

     **************************

    介绍下ConditionObject:

      这个是条件队列,相信很多人看到这个类的时候是很迷惑的,我也是。别慌先看类的方法:

     就俩类方法await和signal,分别是阻塞和唤醒。还有两个属性Node,说明这是个双端链表结构的组织,因为有前驱和后驱两个节点。用大母脚趾头想都能想到这个类是干啥用的。就不赘述了。

    ---------------------------------------------

    ReentrantLock测试:

    ConditionObject测试:

      线程2和线程3是前两个进入锁的线程因为各自调用count+1后,但是不满足Condition的条件,因此被阻塞到Condition1对象上,然后后面进来的所有线程都满足Condition队列的要求,因此可以直接执行。可以看到除了第2和第3线程外,其他线程都是顺序执行(按照非公平锁的CLH队列顺序执行)。

      调用Lock的newCondition方法会创建一个绑定到此Lock上的阻塞队列Condition,这个Condition又叫做条件队列,当没有满足此Condition的条件前,锁是由获得Lock的当前线程占有,当满足Condition的条件后,可以调用Condition.await方法,这样当前线程会以原子的方式释放掉此Lock,然后阻塞在Condition的阻塞队列中,直到被同一个Condition唤醒或者此线程被打断。

    --------------------------

    手写独占锁:

    /**
     * @author yangshengqiang
     * @date 2021/12/17 8:49
     * @description
     */
    public class myLock implements Lock {
        private sync sync = new sync();
    
        private class sync extends AbstractQueuedSynchronizer{
    
            protected boolean tryAcquire(int arg) {
                final Thread thread = Thread.currentThread();
                int state = getState();
                if(state==0){
                    if (compareAndSetState(0, arg)){
                        setExclusiveOwnerThread(thread);
                        return true;
                    }
                    return false;
                }else if(state>0){
                    if(getExclusiveOwnerThread()==thread){
                        setState(state+1);
                        return true;
                    }
                }
                return false;
            }
    
            protected boolean tryRelease(int arg) {
                final Thread thread =Thread.currentThread();//当前线程
                int state = getState();//独占锁状态
                if(state<=0){//
                    throw new RuntimeException("状态异常");
                }
                if(thread==getExclusiveOwnerThread()){//重入锁
                    if(state==0){//设置锁状态为0
                        setExclusiveOwnerThread(null);//清除独占线程
                        return true;
                    }
                    if(compareAndSetState(state, state-1)){//锁状态数量-1
                        setExclusiveOwnerThread(null);
                        return true;
                    }
                }
                return false;
            }
        }
    
        @Override
        public void lock() {
            this.sync.acquire(1);
        }
    
        @Override
        public void unlock() {
            this.sync.release(1);
        }
    
        @Override
        public void lockInterruptibly() throws InterruptedException {
    
        }
    
        @Override
        public boolean tryLock() {
            return false;
        }
    
        @Override
        public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
            return false;
        }
    
        @Override
        public Condition newCondition() {
            return null;
        }
    }
    public class demo1 {
        static Logger logger = LoggerFactory.getLogger(demo1.class);
    
        static myLock myLock = new myLock();
        public static void main(String[] args) {
            new Thread(()-> {
                try {
                    speak();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            },"线程1").start();
            new Thread(()-> {
                try {
                    speak();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            },"线程2").start();
        }
        static int demo = 0;
    
        public static void speak() throws InterruptedException {
            logger.info(Thread.currentThread().getName()+"-准备进入");
            myLock.lock();
            logger.info(Thread.currentThread().getName()+"-已进入"+(demo=++demo));
            Thread.sleep(1000);
            logger.info(Thread.currentThread().getName()+"-已解锁");
            myLock.unlock();
        }
    }
    

     可以看出我们写tryRelease也是为了设置state状态值以及释放独占线程。

     再看acquire方法,我们定义的tryAcquire就是为了设置state和独占线程,至于阻塞得不到锁的线程,由AQS的acquireQueued方法实现。

    所以AQS帮我们实现阻塞和唤醒的机制,而上锁的前提state==o和独占线程是本线程这些需要自己去写,因此我们使用AQS定义自己的同步器锁的时候,需要做的就是设置这两个方法,在上锁前处理好state,在下锁后同样处理好state字段值。

    !!!!

    如有错误,请指正。

  • 相关阅读:
    Java8 新特性(一)- Lambda 表达式
    省选知识复习
    [JSOI2016]灯塔/[POI2011]Lightning Conductor
    [十二省联考2019]春节十二响
    [tyvj1858]XLKxc
    [CF995F]Cowmpany Cowmpensation
    [集训队互测]calc
    拉格朗日插值法入门
    [TJOI2017]异或和
    [noi.ac省选模拟赛]第12场题解集合
  • 原文地址:https://www.cnblogs.com/YsirSun/p/15643167.html
Copyright © 2011-2022 走看看