线程之间的通信
使用线程来运行多个任务时,可以通过使用锁来同步俩个任务的行为,从而使得一个任务不会干扰到另外一个任务,这是解决线程间彼此干涉的问题,现在我们需要来解决线程间彼此协调的问题问题,也就是线程之间通信的问题。–摘自百度
其实我在工作的期间一直对线程这个概念并不是很清晰,只要不是单独线程独立运行的话,就都会涉及线程之间的交互,那么就属于线程之间通信
- join:一个线程让另外一个线程参见进来执行完成
- 生产者/消费者:是生产者和消费者之间的协调
一 wait 和notify/notifyAll
在进行本小节的深入探讨的之前,我想抛出一个问题:什么是线程通信
多个线程在处理同一个资源,并且任务不同时,需要线程通信来帮助解决线程之间对同一个变量的使用或者操作。换句简单的话来描述就是说:多个线程在操作同一份数据时,避免对同一个变量进行操作从而造成变量资源的争夺。
那么在面对这个问题的时候,我们就会想到,要是其中一个线程能睡着
然后等休息好了,在对任务进行操作,这样就不会争抢而且还会有一种井然有序的感觉。
这样的话我们就引出唤醒机制:(wait(),notify())
就是在一个线程进行规定操作后,那么我就进入等待状态(wait),然后等其他线程将指定的任务完成之后,在将线程唤醒(notify)。
wait()方法
在其他线程调用对象时的notify
方法或者 notifyAll
方法前,那么我当前的线程就会等待。
线程调用wait()方法得时候,就会释放他对锁的拥有权,然后等待另外的线程来通知它,当然,唤醒线程的方式不可能是_‘喂!你该起来上班啦!!!’_他是有固定方法的。比如notify
和 notifyAll
方法,使用这俩个方法才可以获得锁的拥有权。
~注意
要确保调用wait()方法的时候拥有锁那么就得确保wait()方法的调用必须放在synchronized方法或synchronized块中。
notify()方法
notify()方法会唤醒一个等待当前对象锁的线程
notifyAll()方法
notifyAll()方法会唤醒此对象监视器上等待的所有线程
~注意
如果多个线程在等待,它们中的一个将会选择被唤醒。
这种选择是随意的,和具体实现有关。
notify()方法应该是被拥有对象的锁的线程所调用。
notifyAll()方法同理
二 线程实例
其实不难理解线程之间最好的实例就是:生产者消费者模型,那么我们就来仿写一个生产者消费者的例子;
生产牛奶的方法以及消费的方法
package ThreadDemo;
/**
* Created by jdx on 2021/12/29 下午3:56
*/
public class Milk {
//生产牛奶的奶牛编号
private int cowsId;
//牛奶
private int bottles;
//生产牛奶的方法
public synchronized void MilkTheCow() {
//当牛奶生产的牛奶瓶子数不为0时,那生产牛奶的方法就停止
if (bottles != 0) {
try {
//等待线程
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//那么当我的牛奶瓶数为0的时候奶牛就开始产奶
bottles = bottles + 1;
//为了区别是哪一个牛产的哪一瓶奶那么奶牛编号也得区分一下
cowsId = cowsId + 1;
//获取生产奶的奶牛
String cowName = Thread.currentThread().getName();
//从控制台输出
System.out.println(cowName + " 生产了一瓶编号为" + cowsId + " 牛奶");
//从这时候开始,就得唤醒其他等待的线程
notify();
}
//消耗牛奶的方法
public synchronized void drinkMilk() {
//当牛奶瓶数为0的时候,这个方法处于等待状态
if (bottles == 0) {
try {
wait();
//InterruptedException 是 wait sleep 等方法自己抛出的 '中断异常'
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//消费牛奶
bottles = bottles - 1;
//人作为消费者
String people = Thread.currentThread().getName();
//从控制台输出
System.out.println(people + "喝了一个牛奶,编号为:" + cowsId);
//执行完成之后 去唤醒其他线程
notify();
}
//get set 方法
public int getCowsId() {
return cowsId;
}
public void setCowsId(int cowsId) {
this.cowsId = cowsId;
}
public int getBottles() {
return bottles;
}
public void setBottles(int bottles) {
this.bottles = bottles;
}
//无参构造
public Milk() {
super();
}
//有参构造
public Milk(int cowsId, int bottles) {
super();
this.cowsId = cowsId;
this.bottles = bottles;
}
}
生产牛奶的类
package ThreadDemo;
/**
* Created by jdx on 2021/12/30 下午6:01
*/
public class cows extends Thread {
//获得牛奶的类
private Milk milk;
//无参构造
public cows() {
super();
}
//有参构造
public cows(Milk milk) {
super();
this.milk = milk;
}
//get set 方法
public Milk getMilk() {
return milk;
}
public void setMilk(Milk milk) {
this.milk = milk;
}
//重写run方法
@Override
public void run() {
productMilk();
}
private void productMilk() {
//默认生产25瓶牛奶
for (int i = 0; i < 2; i++) {
//调用生产牛奶的方法
milk.MilkTheCow();
}
}
}
消费牛奶的类
package ThreadDemo;
/**
* Created by jdx on 2021/12/30 下午6:12
*/
public class people extends Thread {
//获得牛奶的类
private Milk milk;
//get set 方法
public Milk getMilk() {
return milk;
}
public void setMilk(Milk milk) {
this.milk = milk;
}
//重写run方法
@Override
public void run() {
drink();
}
private void drink() {
for (int i = 0; i < 2; i++) {
//调用消费牛奶的方法
milk.drinkMilk();
}
}
//有参构造
public people(Milk milk) {
super();
this.milk = milk;
}
//无参构造
public people() {
super();
}
}
测试单元
package basics;
import ThreadDemo.Milk;
import ThreadDemo.cows;
import ThreadDemo.people;
/**
* Created by jdx on 2021/12/30 下午6:17
*/
public class milkTest {
public static void main(String[] args) {
//new 一个 Milk 类
Milk milk = new Milk();
//new 一个 cows 类
cows cow = new cows(milk);
//new 一个 people 类
people humanity = new people(milk);
//new 一个 包含生产者的线程
Thread threadCow = new Thread(cow, "生产者:奶牛");
//new 一个 包含消费者的线程
Thread threadHum = new Thread(cow, "消费者:人类");
//启动线程
threadCow.start();
threadHum.start();
}
}
多多感悟