参考了很多网上的资料和视频,发现Condition监视器的精准唤醒例子都需要使用标识符,但实际上使用标识符的话是可以不用到Condition监视器的直接用Object监视器(wait/notify)来执行的。
个人感觉这些例子举得都不好,所以自己写了不用标识符的情况下可以直接用Condition来实现精准唤醒的例子,也从中发现了一些问题,具体已经写在注释。
public class TestJUC05 {
public static void main(String[] args) throws InterruptedException {
Data data = new Data();
new Thread(()->{
for (int i = 0; i < 10; i++) {
data.B();
}
},"BB").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
data.C();
}
},"CC").start();
//让A线程最后最后执行才对下一个线程的condition监视器唤醒起效,才能进入一个循环,
//否则一开始A线程执行得太快,有可能会导致当B线程的监视器还没等待的时候已经执行唤醒,
//然后之后就全部线程都无法唤醒,进入死循环
Thread.sleep(1000);
new Thread(()->{
for (int i = 0; i < 10; i++) {
data.A();
}
},"AA").start();
}
}
class Data{
private Lock lock=new ReentrantLock();
Condition condition1=lock.newCondition();
Condition condition2=lock.newCondition();
Condition condition3=lock.newCondition();
//该标识符用于标识A线程是否第一次执行
int number=0;
public void A(){
lock.lock();
try {
//第一次不用等待直接执行,之后的都需要等待线程C的唤醒
if (number!=0) {
condition1.await();
}
System.out.println("AAAAAAAAAAAAAA");
number++;
//让BC线程先跑都进入等待,才能对B线程进行唤醒
condition2.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void B(){
lock.lock();
try {
condition2.await();
System.out.println("BBBBBBBBBBBBBBBB");
condition3.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void C(){
lock.lock();
try {
condition3.await();
System.out.println("CCCCCCCCCCCCCCCC");
condition1.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
//运行结果
AAAAAAAAAAAAAA
BBBBBBBBBBBBBBBB
CCCCCCCCCCCCCCCC
AAAAAAAAAAAAAA
BBBBBBBBBBBBBBBB
CCCCCCCCCCCCCCCC
AAAAAAAAAAAAAA
BBBBBBBBBBBBBBBB
CCCCCCCCCCCCCCCC
AAAAAAAAAAAAAA
BBBBBBBBBBBBBBBB
CCCCCCCCCCCCCCCC
......