案例一
public class AqsThread extends Thread {private Lock lock;public AqsThread(String name, Lock lock) {super(name);this.lock = lock;}@Overridepublic 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() {@Overridepublic 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() {@Overridepublic 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源码.这些运用基本上会很好理解.多读多看.每一次重复的看,都会有新的理解.