java 提供了几个方法解决线程之间的通信问题
- wait() :表示线程一直处于等待状态,直到其他线程通知,与sleep不用,会释放锁
- wait(Longtimeout):指定等待的毫秒数
- notify():唤醒处于的等待状态的一个线程
- notifyAll():唤醒同一个对象上所有调用wait()方法的线程,优先级别高的线程优先调度
注意:他们均是Objet类的方法,都只能在同步方法或者同步代码块儿中使用,否则会抛出异常IllegalMonitorStateException。
解决一:并发协作模式“生产者/消费者模式”–>管程法
- 生产者:负责生产数据的模块(可能是方法、对象、线程、进程);
- 消费者:负责处理数据的模块(可能是方法、对象、线程、进程);
- 缓冲区:消费者不直接使用生产者的数据,他们之间有一个缓冲区,生产者将生产的数据放入缓冲区,消费者从缓冲区拿出数据处理
//生产者、消费者、产品、缓冲区
public class TestProvideAndCustomer {
public static void main(String[] args) {
SynContainer synContainer = new SynContainer();
new Provide(synContainer).start();
new Customer(synContainer).start();
}
}
//生产者
class Provide extends Thread{
SynContainer container;//生产的东西需要放入容器中提供给消费者
public Provide(SynContainer container){
this.container = container;
}
//生产者的方法只进行生产
@Override
public void run() {
for (int i = 0; i < 100; i++) {
container.push(new Chicken(i));//放入容器中
System.out.println("生产出"+i+"只鸡");
}
}
}
//消费者
class Customer extends Thread{
SynContainer container;//需要从容器中取出产品进行消费
public Customer(SynContainer container){
this.container = container;
}
//消费者的方法消费者只进行消费
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("消费了==》"+container.pop().id+"只鸡");
}
}
}
//产品
class Chicken{
int id;//产品编号
public Chicken(int id){
this.id = id;
}
}
//缓冲区
class SynContainer{
//需要一个容器的大小
Chicken[] chickens = new Chicken[10];
//容器计数器
int count = 0;
//生产者放入产品
public synchronized void push(Chicken chicken){
//如果容器满了,就需要等待消费者消费
if(count == chickens.length){
//通知消费者进行消费,生产者进入等待状态
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//如果没有满,就需要生产者丢入商品
chickens[count] = chicken;
count++;
//放入商品之后通知消费者消费
this.notifyAll();
}
//消费者消费商品
public synchronized Chicken pop(){
//判断是否还可以消费
if(count == 0){
//等待生产者生产,消费者等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//如果可以消费
count--;
Chicken chicken = chickens[count];
//通知生产者进行生产
this.notifyAll();
return chicken;
}
}
解决二:信号灯法(利用标识位)
demo
//信号灯法
public class ProvideAndCustom {
public static void main(String[] args) {
TV tv = new TV();
new Provide(tv).start();
new Custom(tv).start();
}
}
//生产者,演员
class Provide extends Thread{
private TV tv;
public Provide(TV tv){
this.tv = tv;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
if(i%2 == 0){
this.tv.played("赵云长坂坡砍曹操直播...");
}else {
this.tv.played("华佗刮骨疗毒直播---");
}
}
}
}
//消费者,观众
class Custom extends Thread{
private TV tv;
public Custom(TV tv){
this.tv = tv;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
this.tv.watched();
}
}
}
//提供平台给生产者和消费者
class TV{
private String voice;//生产者提供的产品
boolean flag = true;//定义一个标识位
//演员表演
public synchronized void played(String voice){
//判断是否生产
if(!flag){
try {
//生产者产出,等待消费者消费
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("演员表演了:"+voice);
this.notifyAll();//通知观众观看
this.voice = voice;
this.flag = !this.flag;//更新标识位
}
//观众观看
public synchronized void watched(){
//判断是否可以消费
if(flag){
try {
this.wait();//等待演员表演
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("观众观看了:"+voice);
this.notifyAll();//通知演员开始表演
this.flag = !this.flag;//更新标识位
}
}
管程法定义一个容器,根据容器的容量来进行互相的通信;信号灯法则是定义一个标识位,利用标识位来互相的通信。