AQS机制详解

案例一

public class AqsThread extends Thread {

    private Lock lock;

    public AqsThread(String name, Lock lock) {
        super(name);
        this.lock = lock;
    }

    @Override
    public void run() {
        lock.lock();
        try {
            System.out.println(Thread.currentThread().getName() + "running");
        } finally {
            lock.unlock();
        }
    }
}
public class AqsDemo {

    public static void main(String[] args) {
        ReentrantLock lock = new ReentrantLock();
        AqsThread t1 = new AqsThread("t1", lock);
        AqsThread t2 = new AqsThread("t2", lock);
        
        t1.start();
        t2.start();
    }
}

执行案例的运行结果.(其中之一) 

 

线程t1先执行lock操作,获取锁.然后线程t2执行lock操作.然后t1进行unlock操作,然后t2获取锁成功,接着执行unlock操作.

 t1线程调用lock.lock方法,方法调用顺序.

t2线程调用lock.lock方法,其方法调用顺序. 

最后的结果是被阻塞.源码如下.

 

t1线程调用lock.unlock,其方法调用顺序.

t1线程中调用lock.unlock后,经过一系列的调用,最终的状态是释放了许可,因为调用了LockSupport.unpark。这时,t2线程就可以继续运行了。此时,会继续恢复t2线程运行环境,继续执行LockSupport.park后面的语句.

 进一步调用.

 

t2线程调用lock.unlock,其方法调用顺序 

 

t2线程执行lock.unlock后,最终达到的状态还是与之前的状态一样.

案例二 

public class DepotDemo {

    private int size;
    private int capacity;
    private Lock lock;
    private Condition fullCondition;
    private Condition emptyCondition;

    public DepotDemo(int capacity) {
        this.capacity = capacity;
        lock = new ReentrantLock();
        fullCondition = lock.newCondition();
        emptyCondition = lock.newCondition();
    }

    public void produce(int no) {
        int left = no;
        lock.lock();
        try {
            while (left > 0) {
                while (size >= capacity)  {
                    System.out.println(Thread.currentThread().getName() + " before await");
                    fullCondition.await();
                    System.out.println(Thread.currentThread().getName() + " after await");
                }
                int inc = (left + size) > capacity ? (capacity - size) : left;
                left -= inc;
                size += inc;
                System.out.println("produce = " + inc + ", size = " + size);
                emptyCondition.signal();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void consume(int no) {
        int left = no;
        lock.lock();
        try {
            while (left > 0) {
                while (size <= 0) {
                    System.out.println(Thread.currentThread() + " before await");
                    emptyCondition.await();
                    System.out.println(Thread.currentThread() + " after await");
                }
                int dec = (size - left) > 0 ? left : size;
                left -= dec;
                size -= dec;
                System.out.println("consume = " + dec + ", size = " + size);
                fullCondition.signal();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}
public class ReentrantLockTest {

    static class Consumer {
        private DepotDemo depot;

        public Consumer(DepotDemo depot) {
            this.depot = depot;
        }

        public void consume(int no) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    depot.consume(no);
                }
            }, no + " consume thread").start();
        }
    }

    static class Producer {
        private DepotDemo depot;

        public Producer(DepotDemo depot) {
            this.depot = depot;
        }

        public void produce(int no) {
            new Thread(new Runnable() {

                @Override
                public void run() {
                    depot.produce(no);
                }
            }, no + " produce thread").start();
        }
    }


}

class ReentrantLockDemo {
    public static void main(String[] args) throws InterruptedException {
        DepotDemo depot = new DepotDemo(500);
        new ReentrantLockTest.Producer(depot).produce(500);
        new ReentrantLockTest.Producer(depot).produce(200);
        new ReentrantLockTest.Consumer(depot).consume(500);
        new ReentrantLockTest.Consumer(depot).consume(200);
    }
}

 

通过静态分析.可以得出如下调用流程.

 

p1线程调用lock.lock,获得锁,继续运行. p2线程调用lock.lock,条件不满足,被放入条件队列阻塞等待唤醒.

c1线程调用lock.lock.

 

c2线程调用lock.lock.

 

通过上面的分析.阻塞的时候会释放锁进入条件队里里等待. 被唤醒后会重新进入抢锁队里.

p1线程执行emptyCondition.signal,其方法调用顺序.

 

p1线程执行lock.unlock,示意图如下.

 

p2成为头节点.获取到锁继续执行.c1和c2还处于阻塞状态. 

p2线程执行fullCondition.await,其方法调用顺序.

新生成一个节点放入到条件队列,并且释放锁.

 

 

继续运行c1线程,c1线程由于之前被park了,所以此时恢复,继续之前的步骤,即还是执行前面提到的acquireQueued方法,之后,c1判断自己的前驱结点为head,并且可以获取锁资源. 

 

c1线程执行fullCondtion.signal,方法调用顺序 .

signal方法达到的最终结果是将包含p2线程的结点从condition queue中转移到sync queue中,之后condition queue为null,之前的尾结点的状态变为SIGNAL。

 

 

c1线程执行lock.unlock操作,根据之前的分析,经历的状态变化.

  

c2线程执行emptyCondition.await

 

await操作将会生成一个结点放入condition queue中与之前的一个condition queue是不相同的,并且unpark头节点后面的结点,即包含线程p2的结点。

p2线程被unpark,故可以继续运行,经过CPU调度后,p2继续运行,之后p2线程在AQS:await方法中被park,继续AQS.CO:await方法的运行.

 

p2继续运行,执行emptyCondition.signal.

 

最终,将condition queue中的结点转移到sync queue中,并添加至尾部,condition queue会为空,并且将head的状态设置为SIGNAL。

p2线程执行lock.unlock操作,根据前面的分析可知.

 

unlock操作会释放c2线程的许可,并且将头节点设置为c2线程所在的结点。

整个流程就结束了.只要仔细研究AQS源码.这些运用基本上会很好理解.多读多看.每一次重复的看,都会有新的理解.

知行合一,先知,知到行,最后知行合一.

如果大家喜欢我的分享的话,可以关注下我的微信公众号

心有九月星辰 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值