Java多线程(3)—生产者/消费者

本文介绍了一个典型的线程问题——生产者-消费者模型,并通过Java代码实现了线程间的通信。利用synchronized关键字和Object类中的wait()/notify()方法,解决了生产者与消费者之间的同步问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这个是一个典型的线程问题。生成者(Productor)生产商品并交给店员(Clerk),消费者(Customer)从店员处购买商品,店员一次性只能购买固定数量的产品,如果生产者试图生成更多的商品,店员会叫生产者暂停一下,如果店里仓库有空位再通知生产者继续生产,如果店中没有商品了,就会告诉消费者等一下,如果店中有商品了就通知消费者来购买。这个场景和我们实际情况也比较贴切,这样就会想可能存在如下问题:

  1. 生产者比消费者快的时,消费者会漏掉部分数据取不到(产品过剩导致浪费,出现滞销)
  2. 消费者比生产者快时,消费者会取到相同的数据(产量不足,供不应求)

在解决这个方法之前先介绍一下线程通信

所谓线程通信可以这样理解:线程与线程之间不是相互独立的个体,它们彼此之间需要相互通信和协作,最典型的例子就是生产者-消费者问题:当队列满时,生产者需要等待队列有空间才能继续往里面放入商品,而在等待的期间内,生产者必须释放对临界资源(即队列)的占用权。因为生产者如果不释放对临界资源的占用权,那么消费者就无法消费队列中的商品,就不会让队列有空间,那么生产者就会一直无限等待下去。因此一般情况下,当队列满时,会让生产者交出对临界资源的占用权,并进入挂起状态。然后等待消费者消费了商品,然后消费者通知生产者队列有空间了。同样地,当队列空时,消费者也必须等待,等待生产者通知它队列中有商品了。这种互相通信的过程就是线程间的协作

1.notify/wait

前面的文中通过包括 static synchronized 等手段来解决数据共享的问题,即多个线程主动地读取一个共享数据,通过 同步互斥访问机制保证线程的安全性。等待/通知机制主要由Object类中的wait()、notify() 和 notifyAll()三个方法来实现。

wait():令当前线程挂起并放弃CPU、同步资源,让其他线程可以访问并修改共享资源,而当前现场排队等候以获得再次对资源的访问机会

notify():唤醒正在排队等待同步资源的线程中优先级最高者结束等待

notifyAll():唤醒正在排队等待资源的所有线程结束等待。

注意:

  1. 这三个方法均非Thread类中所声明的方法,而是Object类中声明的方法。原因是每个对象都拥有monitor(锁),所以让当前线程等待某个对象的锁,当然应该通过这个对象来操作,而不是用当前线程来操作,因为当前线程可能会等待多个线程的锁,如果通过线程来操作,就非常复杂了。
  2. Java.lang.Object提供的这三个方法只能在synchronized方法或synchronized代码块中使用
package blog;


public class TestAccount {

	public static void main(String[] args) {
		Account account  = new Account();
		Account account1  = new Account();
		
		//多线程对象
		User u_weixin = new User(account, 2000);
		User u_zhifubao = new User(account, 2000);
		
		Thread weixin = new Thread(u_weixin,"微信账户");
		Thread zhifubao = new Thread(u_zhifubao,"支付宝账户");
		
		weixin.start();
		zhifubao.start();
	}
}

class Account{

	public static int money = 3000;
	

	public  void takeMoney5(int m,Account a) {
		synchronized(a){//表示当前对象的代码被加入了synchronized同步锁,this表示当前对象
			String name = Thread.currentThread().getName();
			
			//如果是微信操作则等待,等支付宝操作完成再给微信操作
			if (name.equals("微信账户")) {
				try {
					a.wait();//当前线程等待进入阻塞状态
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			
			if (m > money) {
				System.out.println(name + "操作,账户金额不足:"+money);
			}else {
			
			System.out.println(name + "操作,账户原有金额:"+money);
			System.out.println(name + "操作,取款金额:" + m);
			money = money - m;
			System.out.println(name + "操作,取款后的余额:" + money);
			}
			
			if (name.equals("支付宝账户")) {
				try {
					a.notify();//唤醒当前优先级最高的线程,进入就绪状态
				} catch (Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			
		}

	}
	
}

class User implements Runnable{
	Account account;
	int money;
	public User(Account account,int money) {
		
		this.account = account;
		this.money = money;
	}
	
    @Override
    public void run() {
    	account.takeMoney5(money,account);
    }
}

输出结果如下:

2.生产者/消费者

结合上述介绍的notify/wait机制,下面来实现生产者和消费者

package day15;

public class Test3 {

	public static void main(String[] args) {
		
		Clerk c = new Clerk();
		
		
		//消费时不生产,生产时不消费
		//生产者
		new Thread(new Runnable() {
			
			@Override
			public void run() {
				synchronized (c) {
					while (true) {
						//无限循环表示无限生产
						if (c.productNum == 0) {
							System.out.println("商品数为0,开始生产");
							while (c.productNum < 5) {
								c.productNum++;//生产之后,增加商品
								System.out.println("库存值:"+c.productNum);
								
							}
							System.out.println("产品数为:" + c.productNum +"结束生产");
							
							c.notify();//唤醒消费者,让生产者线程等待
						}else {
							try {
								c.wait();
							} catch (InterruptedException e) {
								// TODO Auto-generated catch block
								e.printStackTrace();
							}
						}
						
					}
				}
				
			}
		},"生产者").start();
		
		
		//消费时不生产,生产时不消费
		//生产者
		new Thread(new Runnable() {
			
			@Override
			public void run() {
				synchronized (c) {
					while (true) {
						//无限循环表示无限消费
						if (c.productNum == 5) {
							System.out.println("商品数为4,开始消费");
							while (c.productNum  > 0) {
								c.productNum--;//消费产品,产品减少
								System.out.println("库存值:"+c.productNum);
								
							}
							System.out.println("产品数为:" + c.productNum +"结束消费");
							
							c.notify();//唤醒生产者,让消费者线程等待
						}else {
							try {
								c.wait();//消费者线程等待
							} catch (InterruptedException e) {
								// TODO Auto-generated catch block
								e.printStackTrace();
							}
						}
						
					}
				}
				
			}
		},"消费者").start();
	}
}

class Clerk{
	public static int  productNum = 0;
	
}

输出结果如下:

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值