一、为什么要线程通信
- 当多个线程并发执行的时候,默认情况下CPU是随机切换线程的,当我们需要多个线程共同执行任务的时候,并且需要线程有规律的执行,那么线程之间就需要协调通信。
- 换句话说,只要有协作的地方,就一定要有通信。
二、什么是线程通信
- 多线程共享地址空间和数据空间,所以多个线程间的通信是一个线程的数据可以直接提供给其他线程使用,而不必通过操作系统
- 线程通信使线程间能够互相发送信号,同时使线程能够等待其他线程的信号。
三、线程通信常用的几种方式
1.共享变量
- 通过在多个线程中共享同一个变量,变量随着线程的切换不断改变,通过循环忙等待的方式以保证随时获取线程的最新状态
- 共享变量的方式简单粗暴,但是循环忙等待的方式极为消耗性能
- Demo演示
邮递员线程和客户线程共享了expressArrived(快递到了)变量,邮递员送达快递时及那个变量设为true,客户取完快递将变量改为false
package zhang.task;
import java.util.concurrent.TimeUnit;
public class Express {
private boolean expressArrived = false;
public Thread postMan = new Thread(()->{
while(true){
sleep(3);
System.out.println("快递已到");
expressArrived = true;
}
});
public Thread client = new Thread(()->{
while(true){
if(expressArrived){
System.out.println("快递已取");
expressArrived = false;
}else {
System.out.println("快递还没到");
sleep(1);
}
}
});
private void sleep(long s){
try {
TimeUnit.SECONDS.sleep(s);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void startAll(){
postMan.start();
client.start();
}
public static void main(String[] args) {
new Express().startAll();
}
}
2.wait-notify方式
- Object中有3个方法
wait()方法:使此对象的线程进入等待状态。
notify()方法:唤醒在此对象监视器上等待的单个线程。
notifyAll()方法:唤醒在此对象监视器上等待的所有线程。
wait和notify、notifyAll必须在同一个对象上调用才有效果,通常需要将这个对象在不同线程间共享 - 通过wait-notify的方式来进行通信,当一个线程执行完之后通过wait方法进入等待状态,其他线程需要的时候调用notify方法唤醒
- Demo演示
客户线程在没有包裹时调用lock对象的wait方法,使线程等待
邮递员线程在包裹到达是调用lock对象的notify方法,唤醒等待的客户线程
然后,客户就可以收取快递了
package zhang.task;
import java.util.concurrent.TimeUnit;
/**
* @author Lewis
* 另一个送快递的故事
* 使用wait——notify实现线程通信
*/
public class ExpressWaitNotify {
// 快递状态
private boolean expressArrived =false;
/**
通过改对象改变线程的两种状态
*/
private final Object lock =new Object();
public Thread postMan = new Thread(()->{
while (true){
sleep(3);
System.out.println("快递已到");
expressArrived = true;
synchronized (lock){
lock.notify();
}
}
});
public Thread client = new Thread(()->{
while (true){
if(expressArrived){
System.out.println("快递已取");
expressArrived = false;
}else {
System.out.println("快递还没到啊");
synchronized (lock){
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
});
private void sleep(long s){
try {
TimeUnit.SECONDS.sleep(s);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void startAll(){
postMan.start();
client.start();
}
public static void main(String[] args) {
new ExpressWaitNotify().startAll();
}
}
3.管道流通信
这个暂时还不是很会,以后补