题目
提供一个类
public class Foo {
private static void printA() {
System.out.print("A");
}
private static void printB() {
System.out.print("B");
}
private static void printC() {
System.out.print("C");
}
会有3个线程分别调用printA(),printB(),printC()方法;
编程实现输出结果是ABC.
透过问题看本质:
要实现按序打印,也就是printB要在printA执行之后执行, printC要在printB执行之后执行;
1.利用wait()和notifyAll()
private static final Object obj = new Object();
private static int type = 1;
private static void printA() {
synchronized (obj) {
if (type == 1) {
System.out.print("A");
type = 2;
//更新type后唤醒其他线程
obj.notifyAll();
}
}
}
private static void printB() throws InterruptedException {
synchronized (obj) {
while (type != 2) {
//当type!=2时,阻塞当前线程
obj.wait();
}
if (type == 2) {
System.out.print("B");
type = 3;
//更新type后唤醒其他线程
obj.notifyAll();
}
}
}
private static void printC() throws InterruptedException {
synchronized (obj) {
while (type != 3) {
//当type!=3时,阻塞当前线程
obj.wait();
}
if (type == 3) {
System.out.print("C");
}
}
}
@Test
public void test1() throws InterruptedException {
new Thread(new Runnable() {
@Override
public void run() {
printA();
}
}).start();
new Thread(new Runnable() {
@SneakyThrows
@Override
public void run() {
printB();
}
}).start();
new Thread(new Runnable() {
@SneakyThrows
@Override
public void run() {
printC();
}
}).start();
Thread.sleep(3000);
}
思路:
当线程A没执行之前,利用wait()方法阻塞B, C线程; 线程A执行完成之后,更新type值并利用notifyAll()唤醒B,C线程, 使得B线程得以往下执行(此时C线程被唤醒后因为type !=3,因此会被wait()方法再次阻塞);B线程执行完成后更新type值并唤醒线程C,使得线程C得以往下执行;
2. 利用CountDownLatch
//初始化两个值为1的计数器
private static CountDownLatch countDownLatchB = new CountDownLatch(1);
private static CountDownLatch countDownLatchC = new CountDownLatch(1);
private static void printA2() {
System.out.print("A");
countDownLatchB.countDown();//将计数器值减1
}
private static void printB2() throws InterruptedException {
countDownLatchB.await();//当值不为0时,当前线程会被阻塞
System.out.print("B");
countDownLatchC.countDown();
}
private static void printC2() throws InterruptedException {
countDownLatchC.await();
System.out.print("C");
}
@Test
public void test2() throws InterruptedException {
new Thread(new Runnable() {
@Override
public void run() {
printA2();
}
}).start();
new Thread(new Runnable() {
@SneakyThrows
@Override
public void run() {
printB2();
}
}).start();
new Thread(new Runnable() {
@SneakyThrows
@Override
public void run() {
printC2();
}
}).start();
Thread.sleep(3000);
}
CountDownLatch:
CountDownLatch是一个计数器, 它在初始化时要输入一个数值型的参数作为计数器的值; 其有两个主要的方法,await()和countDown(); countDown()方法会将计数器的值减1;await()方法是:当计数器值不为0时,阻塞当前线程;计数器值为0时,唤醒当前线程; 该篇文章有对CountDownLatch的详细解析:如何实现某个线程在其他线程执行完毕之后再执行
思路:
利用CountDownLatch,创建两个值为1的计数器;分别用来阻塞B,C线程;当线程A执行完成之后,将B线程的计数器减1,使其计数器值为0,从而B线程得以唤醒;同理,B线程执行完成之后会唤醒C线程;从而达到按序执行的效果