线程通信——多线程同步(生产者消费者例子)

前言

多线程的学习接近尾声了,这次我们的主题是线程通信

一、线程通信概述

通过加锁的方式保证共享数据访问的完整性,但是并没有规定线程执行的先后顺序。各线程到底谁先执行由操作系统的调度决定
在进行多线程的设计时,还会遇到另一类问题:如何控制相互交互的线程之间的运行顺序,及多线程的同步

二、典型例子

1.生产者和消费者例子

生产者和消费者的问题
1、生产者:先看是否有数据,如果有就等待;如果没有就生产,生产之后通知消费者来消费
2、消费者:先看是否有数据,如果有就消费;如果没有就等待,通知生产者生产数据

2.通过例子理解与剖析

1、线程的安全问题
因为生产者与消费者共享数据缓冲区,不过这个问题可以使用同步解决
2、线程协调问题
要解决问题,就必须让生产者线程在缓冲区满时等待(wait),暂停进入阻塞状态,等下次消费者消耗了缓冲区中的数据的时间,通知(notify)正在等待的线程恢复到就绪状态,重新开始往缓冲区添加数据。同样,也可以让消费者线程在缓冲区进入等待(wait),暂停进入阻塞状态,等到生产者往缓冲区添加数据之后,再通知(notify)正在等待的线程恢复到就绪状态。

三、常用API

wait()使用当前线程放弃同步锁并进入到等待,直到其他线程进入此同步锁并调用notify()或notifyAll()方法唤醒该线程为止
notify()唤醒此同步锁上等待的第一个调用wait()方法的线程
notifyAll()唤醒此同步锁上调wait()方法的所有线程

注意:
wait()、notify()、notifyAll()这三个方法的调用者都应该是同步锁对象;否则就会报异常

四、生产者和消费者例子代码实现

代码如下(银行卡类):

public class Card {
	// 创建number、money、message三个私有属性
	private String number;
	private int money;
	private String message;
	// 无参构造函数
	public Card() {
		
	}
	// 有参构造函数
	public Card(String number, int money, String message) {
		super();
		this.number = number;
		this.money = money;
		this.message = message;
	}
	// 三个属性get set 方法
	public String getNumber() {
		return number;
	}
	public void setNumber(String number) {
		this.number = number;
	}
	public int getMoney() {
		return money;
	}
	public void setMoney(int money) {
		this.money = money;
	}
	public String getMessage() {
		return message;
	}
	public void setMessage(String message) {
		this.message = message;
	}
	// 重写toString()方法
	@Override
	public String toString() {
		return "Card [number=" + number + ", money=" + money + ", message=" + message + "]";
	}
}

代码如下(生产者线程):

public class ProducerRunnable implements Runnable {
	private Card card;
	private int counter = 0;
	

	public ProducerRunnable(Card card) {
		this.card = card;
	}


	@Override
	public void run() {
		while(true) {
			synchronized(card) {
				int money = card.getMoney();
				if(money<=0) {
					if(counter%2 ==0) {
						card.setMessage("dad set money");
						card.setMoney(3000);
					}else {
						card.setMessage("mom set money");
						card.setMoney(5000);
					}
					counter++;
					//唤醒等待的线程
					card.notify();
					
				}else {
					try {
						// 进入等待
						card.wait();
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
		}
	}
}

代码如下(消费者线程):

public class ConsumerRunnable implements Runnable {
	private Card card;
	

	public ConsumerRunnable(Card card) {
		this.card = card;
	}


	@Override
	public void run() {
		while(true) {
			synchronized(card) {
				int money = card.getMoney();
				if(money>0) {
					String message = card.getMessage();
					System.out.println("取钱:"+message+" "+money);
					//取钱后将银行卡余额设置为0
					card.setMoney(0);
					//唤醒等待的线程
					card.notify();
					
				}else {
					try {
						// 进入等待
						card.wait();
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
		}
	}
}

代码如下(测试):

public class Test01 {

	public static void main(String[] args) {
		//创建银行卡对象
		Card card = new Card("6548755", 0, "success");
		// 获取消费者和生产者类
		ProducerRunnable producerRunnable = new ProducerRunnable(card);
		ConsumerRunnable consumerRunnable = new ConsumerRunnable(card);
		// 获取消费者和生产者类对应的线程类
		Thread producerthread = new Thread(producerRunnable);
		Thread consumerthread = new Thread(consumerRunnable);
		//启动线程
		producerthread.start();
		consumerthread.start();
	}
}

代码如下(输出):

取钱:mom set money 5000
取钱:dad set money 3000
取钱:mom set money 5000
取钱:dad set money 3000
取钱:mom set money 5000
取钱:dad set money 3000
取钱:mom set money 5000
取钱:dad set money 3000
取钱:mom set money 5000
取钱:dad set money 3000
取钱:mom set money 5000
取钱:dad set money 3000
取钱:mom set money 5000
取钱:dad set money 3000
取钱:mom set money 5000
取钱:dad set money 3000
取钱:mom set money 5000
取钱:dad set money 3000

总结

输出只是截取了一部分,这个例子一是加强对wait()、notify()的理解和运用,再就是加强对也锁在多线程里的角色担当。

  • 7
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值