最近看了一篇关于线程通信的文章,把其中涉及的代码都看了一下。对其中容易出错的细节进行了批注以及补充,作笔记使用。
原文地址:https://mp.weixin.qq.com/s/weETO43GKX5yEUPCWYi3wQ
感谢作者的分享
在多线程环境中,线程之间经常需要协同合作,线程之间的协同就会涉及到线程通信的问题。
线程之间通信的方式有很多,我们主要说比较常用的4种.
1 .wait+notify 方式
2 .join 方式
3 .CountDownLatch 方式
4 .CyclicBarrier 方式
下面依次介绍。
1 .wait+notify方式
假设此时有6个任务,A1,A2,A3,B1,B2,B3。
我们需要让A1先执行,然后执行B1,B2,B3。然后再执行A2,A3。
public static void demo2() {
final Object lock = new Object();
final Thread A = new Thread(new Runnable() {
@Override
public void run() {
synchronized (lock){
System.out.println("A1");
try {
//调用wait,notify,notifyAll时,必须要拥有该对象的锁
//如果没有拥有该对象的锁却调用该对象的wait,notify,notifyAll方法
//就会报出IllegalMonitorStateException异常
System.out.println("waiting......");
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("A2");
System.out.println("A3");
}
}
});
Thread B = new Thread(new Runnable() {
@Override
public void run() {
synchronized (lock){
System.out.println("B1");
System.out.println("B2");
System.out.println("B3");
lock.notify();
}
}
});
A.start();
B.start();
}
关于此处为何要使用synchronized同步代码块,注释处做了说明。
下面简单说一下流程
demo1方法体中共新建了2个线程,分别为线程A和线程B。
1 .线程A首先启动,获取lock锁,进入synchronized同步代码块。
2 .执行打印语句 System.out.println(“A1”);
3 .然后执行lock.wait( )进入等待状态,等待执行下面的语句。同时释放lock对象锁。
4 .线程B启动,没有lock锁,等待A线程释放锁,阻塞。
5 .A释放锁,线程B获取到lock锁,进入线程B的synchronized同步代码块
6 .执行打印语句
System.out.println(“B1”);
System.out.println(“B2”);
System.out.println(“B3”);
7 .执行lock.notify( )唤醒正在等待的A线程,A线程执行剩下的打印语句
执行结果如下:
A1
waiting......
B1
B2
B3
A2
A3
2 .join方式
thread.join意为:等待该线程死亡。如果一个线程A执行了thread.join()语句,其含义是:当前线程A等待thread线程终止之后才从thread.join()返回。
例如:我们有6个任务,A1,A2,A3,B1,B2,B3
我们想要依次执行这些任务,即A1, A2, A3, B1,B2,B3。
代码实现如下:
public static void demo1() throws InterruptedException {
final Thread A = new Thread(new Runnable() {
@Override
public void run() {
printNumber("A");
}
});
Thread B = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("B开始等待A");
try {
A.join();//join方法意为等待该线程终止,A执行完之后开始执行B
} catch (InterruptedException e) {
e.printStackTrace();
}
printNumber("B");
}
});
A.start();
B.start();
}
执行结果如下:
B开始等待A
A print: A1
A print: A2
A print: A3
B print: B1
B print: B2
B print: B3
流程如下:
新建两个线程,线程A和B
1 .在线程B中进行A.join()。意为:等待线程A执行结束
2 .线程A结束之后,线程B开始执行
3 .CountDownLatch类
CountDownLatch是ava.util.concurrent包中的类
例如:我们有4个线程,线程A,B,C,D
我们需要A,B,C同步执行。最后再执行D
代码如下:
public static void demo3() {
final CountDownLatch countDownLatch = new CountDownLatch(3);
new Thread(new Runnable() {
@Override
public void run() {
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("All Done ,D start working...");
System.out.println("D");
}
}).start();
for (char i = 'A'; i <= 'C'; i++) {
final String tn = String.valueOf(i);
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(tn + " is working...");
countDownLatch.countDown();
}
}).start();
}
}
执行结果如下:
A is working...
C is working...
B is working...
All Done ,D start working...
D
流程如下:
1 .CountDownLatch 类构造器中初始化3个线程,意为:当3个线程执行完之后,最后执行D
2 .await方法。先调用await方法判断CountDownLatch中是否有初始化的线程,有的话就等待。
3 .countDown方法,每次执行一个线程后,使用该方法将CountDownLatch 中的线程数减一。当CountDownLatch 中的线程数为0时,立即唤醒正在await的方法。
4 .CyclicBarrier类
假设我们有以下需求。我们有4个线程A,B,C。我们想要3个线程都准备好之后,再一起进行执行。
代码如下:
public static void demo4() {
final CyclicBarrier cyclicBarrier = new CyclicBarrier(3);
for (char i = 'A'; i <= 'C'; i++) {
final String tn = String.valueOf(i);
new Thread(new Runnable() {
@Override
public void run() {
try {
System.out.println(tn+" 准备好了,等待其他人");
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
System.out.println(tn+" 开始运行");
}
}).start();
}
}
执行结果如下:
A 准备好了,等待其他人
C 准备好了,等待其他人
B 准备好了,等待其他人
A 开始运行
C 开始运行
B 开始运行
流程如下:
1 .我们初始化好3个线程,然后在每个线程中都调用cyclicBarrier.await()方法。进入等待状态,等待其他线程准备好
2 .当所有的线程都执行了该方法之后,CyclicBarrier类判断到此时所有的线程都已经准备好。此时唤醒等待的所有线程。同时执行线程中的方法。