线程协作(通信) 16
-
应用场景:1. 假设仓库中只能存放一件产品,生产者将生产出来的产品放入仓库,消费者将仓库中产品取走消费。
2.
被消费者取走为止。
3. 如果仓库放有产品,则消费者可以将产品取走消费,否则停止消费并等待,直到仓库中再次
放入产品为止。
这是一个线程同步问题,生产者和消费者共享同一个资源,并且生产者和消费者之间相互依赖,互为条件。
-
java提供了几个方法解决线程之间的通信问题:

注意:均是Object类的方法,都只能在同步方法或者同步代码块中使用,否则会抛出异常IIIegaIMoitorStateException。
-
解决式:1. 并发协作模型“生产者/消费者”--->管程法
2. 生产者:负责生产数据的模块(可能是方法,对象,线程,进程)
3. 消费者:负责生产数据的模块(可能是方法,对象,线程,进程)
4. 缓冲区:消费者不能直接使用生产者的数据,它们之间有个“缓冲区”
生产者将生产好的数据放入缓冲区,消费者从缓冲区拿出数据。
第二种:并发协作模型“生产者/消费者模式”-->信号灯法(trun , Flase)
package Runnable1;
/*
线程通信:使用两个线程打印1-100,线程1,线程2 交替打印
涉及三个方法:
wait():一旦执行此方法,当前线程就进入阻塞状态并释放同步监视器(锁)
notify():一旦执行此方法,就会唤醒被wait的一个线程,如果有多个线程被wait,就唤醒优先级高的线程
notifyAll:一旦执行此方法,就会唤醒所有被wait的线程
说明:
只能是出现在同步代码块或者同步方法中,调用者必须是同步代码块或者同步方法中同步监视器。
否则,会出现异常
三个方法是定义在java.lang.Object类中
面试题:sleep和wait的异常
相同点:一旦执行方法,都可以使得当前的线程进入阻塞状态。
不同点:1. 两个方法声明的位置不同,Thread类中声明sleep(),Object列类中声明wait()
2. 调用的要求不同:sleep()可以在任何需要的场景下调用,wait()必须使用同步代码块或同步方法中
3. 关于是否是否同步监视器:如果两个方法都使用在同步代码块或者同步方法中,sleep()不会释放锁,wait()会释放锁
*/
class Number implements Runnable
{
private int numeber=1;
package Runnable1;
/*
线程通信的应用:生产者消费者问题
分析:
是否是多线程问题?是,生产者线程,消费者线程
是否有共享数据?是店员(或产品)
如何来解决线程安全问题?同步机制:三种方法
是否涉及线程的通信?是
*/
class Clerk//
{
private int productCount=0;
//生产产品
//在这两个线程中一个时间内只有一个线程去跑
public synchronized void ProduceProduct()
{
if (productCount<20)
{
productCount++;
System.out.println(Thread.currentThread().getName()+":开始生产第"+productCount+"个产品");
//只要生产了一个产品,就唤醒消费者
notify();
}else {//不生产
try {
wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
//消费产品
public synchronized void consumeProduct()
{
if (productCount>0)
{
System.out.println(Thread.currentThread().getName()+":开始消费第"+productCount+"个产品");
productCount--;
notify();//只要消费一个产品,就把生产者唤醒生产
}else {//小于零
//等待
try {
wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
class Producer extends Thread//生产者
{
private Clerk clerk;//共享数据
public Producer(Clerk clerk)
{
this.clerk = clerk;
}
模拟生产者和消费者的案例:
- 创建馒头类(模拟共用的数据)
public class ManTou {
int id; public ManTou(int id) { this.id=id; } public String toString() { return "ManTou[id="+this.id+"]"; } }
int index=0; ManTou[] manTous=new ManTou[6]; //往篮子中放馒头 public synchronized void push(ManTou manTou) { //篮子满了 需要等待 if(index==6) try { this.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } this.notify(); //唤醒的是pop中的wait方法 一旦放馒头了 则说明篮子中有馒头了 取馒头的方法不用等了 this.manTous[index]=manTou; index++; } //从篮子中取馒头 public synchronized ManTou pop() { if(index==0) try { this.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } this.notify(); //唤醒的是push中的wait方法 一旦取馒头了 则说明篮子有空位了 放馒头的行为就不用等了 index--; return this.manTous[index]; }}
Basket basket=null; public Producer(Basket basket) { this.basket=basket; } //执行任务 public void run() { for(int i=1;i<=20;i++) { ManTou manTou=new ManTou(i); basket.push(manTou); System.out.println("生产了"+manTou); try { this.sleep(200); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }创建消费者(处理数据)
public class Consumer extends Thread{
Basket basket=null; public Consumer(Basket basket) { this.basket=basket; } //执行任务 public void run() { for(int i=1;i<=20;i++) { ManTou manTou= this.basket.pop(); System.out.println("消费了"+manTou); try { this.sleep(500); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }}public class Test {
/** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub Basket basket=new Basket(); Producer producer=new Producer(basket); Consumer consumer=new Consumer(basket); producer.start(); consumer.start(); }}

浙公网安备 33010602011771号