1.概念
Condition主要是为了在J.U.C框架中提供和Java传统的监视器风格的wait,notify和notifyAll方法类似的功能。 JDK的官方解释如下:条件(也称为条件队列 或条件变量)为线程提供了一个含义,以便在某个状态条件现在可能为 true 的另一个线程通知它之前,一直挂起该线程(即让其“等待”)。因为访问此共享状态信息发生在不同的线程中,所以它必须受保护,因此要将某种形式的锁与该条件相关联。等待提供一个条件的主要属性是:以原子方式 释放相关的锁,并挂起当前线程,就像 Object.wait 做的那样。Condition实质上是被绑定到一个锁上。
2.核心
理解ReentrantLock以及condition的关键是要理解它的内部机制,其中核心就是:
ReentrantLock的AQS队列、condition队列、以及condition.await()方法、condition.signal()方法。
我们需要弄清楚的是:
1.AQS队列和codition队列之间是什么关系???
2.condition.await()方法对condition队列以及AQS队列的影响??
3.condition.signal()方法对condition队列以及AQS队列的影响??
4.AQS队列和锁lock之间的关系??
3.用代码验证condition.await()方法
public class TestAwaitMain {
private static ReentrantLock lock;
public static void main(String[] args) {
// TODO Auto-generated method stub
//1.线程A先持有锁
// 然后await
//在B中查询是否能获取锁
//如果能则说明 await()方法可以释放锁
lock=new ReentrantLock();
Condition condition = lock.newCondition();
//开辟两个线程
new Thread(new RunnableA(lock,condition),"t_A").start();
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
new Thread(new RunnableB(lock,condition),"t_B").start();
lock.lock();
System.out.println("线程"+Thread.currentThread().getName()+"Condition队列长度是 :"+lock.getWaitQueueLength(condition));
System.out.println("等待获取lock锁的"+"AQS队列长度是 :"+lock.getQueueLength());
}
}
RunnableA的代码
public class RunnableA implements Runnable {
private ReentrantLock lock;
private Condition condition;
public RunnableA(ReentrantLock lock, Condition condition) {
super();
this.lock = lock;
this.condition = condition;
}
@Override
public void run() {
System.out.println("线程A运行");
//先阻塞
lock.lock();
System.out.println("线程"+Thread.currentThread().getName()+"的lock方法已经执行");
try {
System.out.println("线程"+Thread.currentThread().getName()+"持有锁吗 ?:"+lock.isHeldByCurrentThread());
System.out.println("持有锁的线程数:"+ lock.getHoldCount());
try {
System.out.println("线程"+Thread.currentThread().getName()+"准备执行await()方法");
System.out.println("线程"+Thread.currentThread().getName()+"等待获取锁的队列长度"+lock.getQueueLength());
System.out.println("线程"+Thread.currentThread().getName()+"Condition队列长度"+lock.getWaitQueueLength(condition));
lock.hasWaiters(condition);
condition.await();
//此时,如果不执行await操作
//看看AQS队列的长度
Thread.sleep(10000L);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} finally {
// TODO: handle finally clause
//lock.unlock();
}
}
}
RunnableB的代码
public class RunnableB implements Runnable {
private ReentrantLock lock;
private Condition condition;
public RunnableB(ReentrantLock lock, Condition condition) {
super();
this.lock = lock;
this.condition = condition;
}
@Override
public void run() {
System.out.println("线程B运行");
//先阻塞
lock.lock();
System.out.println("线程"+Thread.currentThread().getName()+"的lock方法已经执行");
try {
System.out.println("线程"+Thread.currentThread().getName()+"持有锁吗 ?:"+lock.isHeldByCurrentThread());
System.out.println("持有锁的线程数:"+ lock.getHoldCount());
try {
System.out.println("线程"+Thread.currentThread().getName()+"准备执行await()方法");
System.out.println("线程"+Thread.currentThread().getName()+"等待获取锁的AQS队列长度是 :"+lock.getQueueLength());
System.out.println("线程"+Thread.currentThread().getName()+"Condition队列长度是 "+lock.getWaitQueueLength(condition));
//lock.hasWaiters(condition);
condition.await();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} finally {
// TODO: handle finally clause
lock.unlock();
}
}
}
运行结果
分析:
可见,在调用condition.await()方法时,其执行流程是这样的:
1.释放当前线程持有的锁
2.把当前线程加入到condition队列的尾部,此时,condition的队列长度增加1。
3.当前线程会在await()方法中不断尝试自旋获取锁。如果获取不到锁,那么线程A就无法从await()方法中返回。
4.用代码验证condition.signal()方法
/**
*
* 验证signal的作用:
*
* 1.线程A先lock
* 2.线程A再await
* 3.线程B执行lock
* 4.线程B执行signal操作
* 5.此时,检验线程Bsignal之后,线程A的await后面的代码能执行吗???
*
* //如果线程B只signal而不释放锁的话,此时线程A能从await中醒来吗????
* @author chihaojie
*
*/
public class TestSignalMain {
private static ReentrantLock lock;
public static void main(String[] args) {
// TODO Auto-generated method stub
//1.线程A先持有锁
// 然后await
//在B中查询是否能获取锁
//如果能则说明 await()方法可以释放锁
lock=new ReentrantLock();
Condition condition = lock.newCondition();
//开辟两个线程
new Thread(new RunnableA(lock,condition),"t_A").start();
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
new Thread(new RunnableB(lock,condition),"t_B").start();
}
}
RunnableA的代码
public class RunnableA implements Runnable {
private ReentrantLock lock;
private Condition condition;
public RunnableA(ReentrantLock lock, Condition condition) {
super();
this.lock = lock;
this.condition = condition;
}
@Override
public void run() {
System.out.println("线程A运行");
//先阻塞
lock.lock();
System.out.println("线程"+Thread.currentThread().getName()+"的lock方法已经执行");
try {
System.out.println("线程"+Thread.currentThread().getName()+"持有锁吗 ?:"+lock.isHeldByCurrentThread());
System.out.println("持有锁的线程数:"+ lock.getHoldCount());
try {
System.out.println("线程"+Thread.currentThread().getName()+"准备执行await()方法");
System.out.println("线程"+Thread.currentThread().getName()+"等待获取锁的队列长度"+lock.getQueueLength());
System.out.println("线程"+Thread.currentThread().getName()+"的Condition队列长度"+lock.getWaitQueueLength(condition));
condition.await();
System.out.println("线程"+Thread.currentThread().getName()+"的await被signal之后,持有锁吗 ?:"+lock.isHeldByCurrentThread());
//signal之后:
//condition队列长度为0,AQS队列长度为1
System.out.println("线程"+Thread.currentThread().getName()+"的await被signal之后:"+"AQS队列长度是 :"+lock.getQueueLength());
System.out.println("线程"+Thread.currentThread().getName()+"的await被signal之后:"+"Condition队列长度是 :"+lock.getWaitQueueLength(condition));
//此时,如果不执行await操作
//看看AQS队列的长度
//醒来之前的必须先获取锁
//That thread must then re-acquire the lock before returning from await.
Thread.sleep(10000L);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} finally {
// TODO: handle finally clause
lock.unlock();
}
}
}
RunnableB的代码
public class RunnableB implements Runnable {
private ReentrantLock lock;
private Condition condition;
public RunnableB(ReentrantLock lock, Condition condition) {
super();
this.lock = lock;
this.condition = condition;
}
@Override
public void run() {
System.out.println("线程B运行");
//先阻塞
lock.lock();
System.out.println("线程"+Thread.currentThread().getName()+"的lock方法已经执行");
try {
System.out.println("线程"+Thread.currentThread().getName()+"持有锁吗 ?:"+lock.isHeldByCurrentThread());
try {
System.out.println("线程"+Thread.currentThread().getName()+"等待获取锁的AQS队列长度是 :"+lock.getQueueLength());
System.out.println("线程"+Thread.currentThread().getName()+"Condition队列长度是 "+lock.getWaitQueueLength(condition));
//lock.hasWaiters(condition);
condition.signal();
//线程B再执行了signal()方法之后,我们看一下此时:AQS队列的情况以及condition队列的情况
System.out.println("线程"+Thread.currentThread().getName()+"执行signal()方法之后等待获取锁的AQS队列长度是 :"+lock.getQueueLength());
System.out.println("线程"+Thread.currentThread().getName()+"执行signal()方法之后Condition队列长度是 "+lock.getWaitQueueLength(condition));
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} finally {
// TODO: handle finally clause
//V2:注释掉lock.unlock();
//即: 如果线程B不释放锁的话,即使线程B调用了signal()方法,那么线程A能从await方法返回吗???
//V3
lock.unlock();
}
}
}
运行结果:
分析结果:
从上面的运行结果,我们可以得知:
signal()方法都做了哪些事情???
signal()方法:
1.将condition队列的头节点,从condition队列中移除
2.把上面移除的节点加入到AQS队列的尾部
3.让其等待再次获取锁。
可见,当线程B执行了signal()之后,AQS队列的长度增加,condition队列的长度减少。并且线程A会一直在await()方法中尝试自旋获取锁
如果获取不到锁,那么线程A就无法从await()方法中返回。
5.附上一张原理图,方便大家理解
6.补充
对于lock.lock()方法的理解:
我们先来看一下,下面的这段代码:
try {
lock.lock();
if (storage > 0) {
putCondition.await();
}
storage++;
System.out.println("put => " + ++putCounter );
getCondition.signal();
} finally {
lock.unlock();
}
我们要知道
ReentrantLock是一个独占式的锁lock.lcck();
1.lock.lock()这是获取锁的操作
2.但是这把锁有可能已经被占用了
3.如果获取锁成功,即线程继续往下执行
4.如果锁已经被其他线程获取了,
5.则该线程会被阻塞住,然后加入到AQS队列中。
即: lock.lock()方法如果获取不到锁的话,就会把当前线程加入到AQS队列的尾部。