【Java学习记录】三十四 线程之间的通信

线程之间通信

当一个线程正在使用一个同步方法时,其他线程就不能使用这个同步方法,因为被上了锁。对于同步方法,有时候会涉及某些特殊的情况,例如生活中一个人在排队买电影票时,如果他给售票员的钱不是零钱,而售票员没有零钱找零给他,那么他就需要等待,并允许后面的其他人买票,以便售票员获得零钱给他。Java语言包含了wait( ),notify( )和notifyAll( )方法协调线程之间的进行进度关系,从而实现线程同步。这些方法在对象中是用final方法实现的,所以所有的类都含有它们,且不能被重写。这三个方法仅在synchronized方法中才能被调用。

  • wait( ) 导致当前线程等待,直到另一个线程为此对象调用notify()方法或notifyAll()方法,与sleep不同,会释放锁。
  • notify( ) 恢复相同对象中第一个调用 wait( ) 的线程。
  • notifyAll( ) 恢复相同对象中所有调用 wait( ) 的线程。具有最高优先级的线程最先运行。

例子

模拟喜羊羊和懒羊羊买电影票,售票员只有两张5元的钱,电影票5元一张,喜羊羊拿了20元一张的钱排在懒羊羊前面买票,懒羊羊拿一张5元的钱买票,因此喜羊羊必须要等待懒羊羊先买票。

//售票处
class TicketsHouse implements Runnable{
	int fiveAmount=2;//2张5元
	int twentyAmount=0;//0张20元
	
	public void run() {
		if(Thread.currentThread().getName().equals("喜羊羊")) {
			saleTickets(20);
		}else if(Thread.currentThread().getName().equals("懒羊羊")) {
			saleTickets(5);
		}
	}
	
	//买票	
	private synchronized void saleTickets(int money) {
		if(money == 5) {
			fiveAmount = fiveAmount+1;
			System.out.println(Thread.currentThread().getName()+"钱刚好,买到了电影票");
		}else if(money == 20) {
			while(fiveAmount<3) {
				try {
					System.out.println(Thread.currentThread().getName()+"靠边等待...");
					wait();
					Thread.sleep(1000);
					System.out.println(Thread.currentThread().getName()+"继续卖票");
					
				} catch (InterruptedException e) {}
			}
			fiveAmount = fiveAmount-3;
			twentyAmount = twentyAmount+1;
			System.out.println(Thread.currentThread().getName()+"给20,找零15,拿到票 ");
		}
		notifyAll();
	}
}

	public class Tickets{
		public static void main(String[] args) {
			TicketsHouse officer = new TicketsHouse();
			
			Thread xyy = new Thread(officer,"xyy");
			Thread lyy = new Thread(officer,"lyy");
			
			xyy.start();
			lyy.start();
		}
	}

运行结果:

喜羊羊靠边等待...

懒羊羊钱刚好,买到了电影票

喜羊羊继续买票

喜羊羊给20,找零15,拿到票

生产者消费者模式

应用场景:生产者和消费者问题

  • 假设仓库中只能存放一件产品,生产者将生产出来的产品放入仓库,消费者将仓库中产品取走消费;
  • 如果仓库中没有产品,那么生产者将产品放入仓库,否则停止生产并等待,直到仓库中的产品被消费者拿走为止;
  • 如果仓库中有产品,那么消费者可以将产品取走,否则停止消费并等待生产,知道仓库中放入产品为止。

生产者和消费者共同分享一个资源,对于生产者,在没有生产产品之前,要通知消费者等待,等生产好产品之后,要通知消费者消费。对于消费者,在消费之后,要通知生产者已经消费结束,需要继续生产商品。它不是一种设计模式,而是一种解决由多线程引发的同步问题的办法,也称为有限缓冲问题。

synchronized可以组织并发更新同一个资源,实现同步,但是不能实现线程之间的通信。

解决方式1:管程法

生产者:负责生产数据的模块(可能是方法、对象、线程、进程)

消费者:负责处理数据的模块(可能是方法、对象、线程、进程)

缓冲区:消费者不能直接使用生产者的数据,它们之间存在“缓冲区”,生产者将生产出来的数据放入“缓冲区”,消费者从“缓冲区”将数据取出处理。

 

package com.sxt.thread;
/**
 * 生产者消费者实现方式一:管程法
 * @author liwenlong
 *
 */
public class CoTest01 {

	public static void main(String[] args) {
		SynContainer container = new SynContainer();
		new Productor(container).start();
		new Constomer(container).start();
	}
}

//生产者
class Productor extends Thread{
	SynContainer container;
	public Productor(SynContainer container) {
		this.container = container;
	}
	
	@Override
	public void run() {
		//生产
		for(int i = 0;i<100;i++) {
			System.out.println("生产第"+i+"个冰淇淋");
			container.push(new Iscream(i));
		}
	}
}

//消费者
class Constomer extends Thread{
	SynContainer container;
	public Constomer(SynContainer container) {
		this.container = container;
	}
	
	@Override
	public void run() {
		//消费
		for(int i = 0;i<100;i++) {
			System.out.println("消费第"+container.getIscream().id+"个冰淇淋"); 
		}
	}
}

//缓冲区
class SynContainer{
	Iscream[] iscreams = new Iscream[10];
	int num = 0; //计数器
	//存储 生产
	public synchronized void push(Iscream iscream) {
		//什么时候生产
		//容器没有空间,不可以生产
		if(num==iscreams.length) {
			try {
				this.wait();//线程阻塞 消费者通知生产解除
			} catch (Exception e) {
				// TODO: handle exception
			}
		}
		//存在空间,可以生产
		iscreams[num]=iscream;
		num++;
		//通知生产
		this.notifyAll();
	}
	//获取 消费
	public synchronized Iscream getIscream() {
		//何时消费 判断容器中是否有数据
		//没有数据只有等待
		if(num == 0) {
			try {
				this.wait();  //线程阻塞 生产者通知消费,解除阻塞
			} catch (Exception e) {
			}
			
		}
		//存在数据可以消费
		num--;
		Iscream iscream = iscreams[num];
		this.notifyAll(); //唤醒对方生产
		return iscream;
	}
}

//冰淇淋
class Iscream{
	int id;

	public Iscream(int id) {
		super();
		this.id = id;
	}
	
}

 

解决方式2:红绿灯法

置一个标识,当标识为真的时候,消费者消费,生产者等待。当标识为假的时候,生产者生产,消费者等待.这种模式像是设置了一个信号灯,因此被称为红绿灯法。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

编程芝士

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值