AQS

先说一个总的:

1.AQS是一个能够构建出多种同步器的一个类。不同的锁类对象调用lock()和unlock()方法时,其实是调用内部的一个继承了AQS类的对象的lock()和release()方法,即其实不同的锁类是依赖AQS的。
只需覆根据锁的不同,覆写继承了AQS类的获取锁和释放锁的逻辑,至于获取锁失败了怎么办,这就是AQS统一规定,已经实现好了。

2.锁如果是排它锁,就覆写方法,
tryAcquire(int) //独占方式。尝试获取资源,成功则返回true,失败则返回false。
tryRelease(int) //独占方式。尝试释放资源,成功则返回true,失败则返回false。

如果是共享锁,就覆写方法。
tryAcquireShared(int) //共享方式。尝试获取资源。有一个最大的资源数量,当获取到锁的线程数大于这个最大值,则说明名额用完了,则获取失败,小于说明还可以获取;0表示成功,但没有剩余可用资源;正数表示成 功,且有剩余资源。
tryReleaseShared(int) //共享方式。尝试释放资源,成功则返回true,失败则返回false。当锁的数量为0时,释放成功。

如果既有共享锁,又有排它锁的组合锁,ReentrantReadWriteLock,覆写上述四个方法。

3.AQS有一个被volatile修饰的int型变量代表多个线程竞争的锁,维护了一个先进先出的列表将获取锁失败的线程加入到队列,让线程去争取锁,直到获取到锁或者判断自己应该被阻塞才不去获取锁,如果获取到锁并在队列的第二个位置时,就会出队。
在这里插入图片描述

Synchronize的锁是对象里的mutex,线程执行到synchronize修饰的方法时,让线程去查看对象的锁计数器来判断锁是否被占用或空闲,那继承自AQS类的锁是什么呢?怎么让线程知道锁是否被其他线程占领了,还是可用呢?

AQS类维护了一个volatile修饰的int类型的state变量,这个被多个线程共享的变量就相当于锁,lock是继承自AQS类的锁类生成的锁对象,将lock.lock()添加到你要锁住的代码前面,当多个线程执行到lock.lock()时,即开始获取锁时,就需要判断lock对象的state变量的状态,通过该变量的状态来判断该锁是否被别的线程占领(修改),比如ReentrantLock,当线程进入lock()方法里,看到state值大于0,就说明有线程已经将原来的0置1了,又因为是可重入锁,再判断当前这把锁记录的线程是不是自己,如果是自己,就不用被阻塞。

ReentrantLock覆写获取锁的方法:tryAcquire

protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }
 final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {  
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }

Synchronize中,线程一旦获取锁失败,会被加入到对象中mutex维护的队列中,让线程等待直到被唤醒。按AQS怎么处理获取锁失败的线程呢?

AQS也维护了一个先进先出的队列,专门用来装入获取锁失败的线程,就是专门处理获取锁失败了怎办逻辑。这个获取锁怎么才算获取失败,怎么才算获取成功由自己定义。
AQS规定: 一旦获取失败,线程就会加入到队列的尾部,并且一直尝试去申请锁,直到线程已经排到队的第二个位置且已经拿到锁,队列删除该线程,或者判断自己是否应该被阻塞,阻塞后等待被唤醒。

addWaiter(Node.EXCLUSIVE), arg)将线程添加到队列尾部,acquireQueued()让线程一直尝试去申请锁,直到线程已经排到队的第二个位置且已经拿到锁,队列删除该线程,或者判断自己是否应该被阻塞,阻塞后等待被唤醒。

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

可重入锁怎么保证可重入的?

可重入锁获取锁的逻辑是:发现锁不等于0的时候,回去判断这把锁是不是由我占领,如果是则不会返回false实现可重入的。

共享锁和排它锁获取锁和释放锁有什么不同?

排它锁获取资源时,是0则表示获取成功,大于0可重入锁如果是当前线程占有,也会成功,锁加1。锁大于0且不是自己占领,返回false.

共享锁获取资源时,共享锁有一个最大共享锁数,有一个最大的资源数量,当获取到锁的线程数大于这个最大值,则说明名额用完了,获取失败,小于说明还可以获取;0表示成功,但没有剩余可用资源;正数表示成 功,且有剩余资源。

排他锁释放锁时,锁减1,唤醒对列中的第二个线程节点。

而共享锁释放资源时,当锁的数量为0时,释放成功,唤醒后继节点。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值