java多线程(三)生产消费模型(wait和notify)

一、Object自带的方法

1.wait

1.1 wait()方法,在其他线程调用notify和notifyAll方法之前,当前线程等待。
什么意思了,就是在当某一个线程在执行一个任务时,这个任务是一个方法,方法中有一个wait方法,当线程执行到这句时,就会停止执行,当前线程处于阻塞状态,但是会释放对象锁,意思就是会把钥匙交出去,这时候其他线程试图拿到了此对象锁,进行执行该任务,只有当执行到了任务里面的notify和notifyAll方法时,那个处于阻塞状态的线程才会被唤醒。

1.2 wait(long timeout)这个方法呢,和上一个方法类似,但是不同的一点是,就算没有其他线程来调用notify方法,当超过了它传入的时候后,线程也会被唤醒。

2.notify和notifyAll

2.1 notify方法刚才应用上上面讲的wait()方法,调用notify方法后,便会唤醒在执行该任务的等待的单个线程。

2.2 notifyAll方法,与上面不同的便是,notify只会唤醒等待的单个线程,但如果有多个线程都是该任务的等待线程,这时便可以使用notifyAll唤醒所以的等待线程。

二、生产消费模式

1.前言

1.1 为了更好的了解wait和notify方法,我们引入了生产消费模式来加强我们的理解,这两个方法也通常用于这种模式中,实现的场景。比如一个公司有内网。现在我们的服务器是在外网中。在外网的服务器如果有一个用户登录了,需要通知到内网进行知晓。这时我们便可以建一个中间服务器来通过生产消费模式来进行监听,当然java中的EJB,JMS这些消息传递也难实现,但这里主要是讲生产消费模式。外网中有一个用户登录,代表生产了一个信息,中间服务器进行接受,然后把这条消息传递到内网,实现消费过程。这个列子可能比较抽象,其实生产消费模式,也就是你负责炸爆米花,我负责吃

2.uml图

2.1 这里我还是把任务和线程分开来写,就在Thread里面写任务内容更方便,我分开的目的还是要明确一点,线程是线程,任务是任务,线程只是执行任务的工具。

在这里插入图片描述

3.代码

3.1 Productor为生产类。用来生产产品,queue为我们传入的仓库对象,仓库是有限的,当仓库的东西满了之后,便停止生产。调用了wait方法,电源就好比是线程,生产机械就是这个类,用来生产东西,当不需要生产时,拔掉电源就好了。

package com.test;

import java.util.Queue;
import java.util.Random;

public class Productor{

	private Queue<String> queue;
	
	private int maxthing;
	public  Productor(Queue<String> queue,int maxthing){
		this.queue=queue;
		this.maxthing=maxthing;
	}
	
	public  void pro(){
		while (true) {
			synchronized(queue){
				while (queue.size()== maxthing) {
					System.out.println("仓库已满"+
							Thread.currentThread().getName()+"进入wait状态");
					try {
						queue.wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
				//仓库没满,可继续生产
				System.out.println(Thread.currentThread().getName()+"生产一个,仓库总量:"+queue.size());
				queue.offer("爆米花");
				queue.notifyAll();
				try {
	                Thread.sleep(new Random().nextInt(1000));
	            } catch (InterruptedException e) {
	                e.printStackTrace();
	            }
			}
		}
	}

}

3.2 Customer为我们的消费类,当仓库没有物品时候,我们就无法消费,便只有等待仓库有物品时来唤醒我们当前的等待线程来消费。

package com.test;

import java.util.Queue;
import java.util.Random;

public class Customer {
	
	private Queue<String> queue;
	
	public  Customer(Queue<String> queue,int maxthing){
		this.queue=queue;
	}
	
	public  void cus(){
		while(true){
			synchronized(queue){
				while (queue.size()== 0) {
					System.out.println("仓库已空"+
							Thread.currentThread().getName()+"进入wait状态");
					try {
						queue.wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
				//仓库没空,可消费
				System.out.println(Thread.currentThread().getName()+"消费一个,仓库总量:"+queue.size());
				queue.poll();
				queue.notifyAll();
				try {
	                Thread.sleep(new Random().nextInt(1000));
	            } catch (InterruptedException e) {
	                e.printStackTrace();
	            }
			}
		}
	}
}

3.3 ThreadProduct生产线程,用来执行我们的生产任务。

package com.test;

public class ThreadProduct extends Thread{

	private Productor productor;
	
	public ThreadProduct(String name,Productor productor) {
		super(name);
		this.productor=productor;
	}
	
	@Override
	public void run() {
		productor.pro();
	}
}

3.4 ThreadCustom消费线程,用来执行我们的消费任务。

package com.test;

public class ThreadCustom extends Thread{
	
	private Customer customer;
	
	public ThreadCustom(String name,Customer customer) {
		super(name);
		this.customer=customer;
	}
	
	@Override
	public void run() {
		customer.cus();
	}
}

3.5 Test4测试类

package com.test;

import java.util.LinkedList;
import java.util.Queue;

public class Test4 {
	
	private static int MAXTHING=10;
	
	private static Queue<String> queue=new LinkedList<String>();
	
	public static void main(String[] args) {
		Productor productor=new Productor(queue, MAXTHING);
		Customer customer=new Customer(queue, MAXTHING);
		Thread pr1=new ThreadProduct("pr1",productor);
		Thread pr2=new ThreadProduct("pr2",productor);
		Thread cu1=new ThreadCustom("cu1",customer);
		Thread cu2=new ThreadCustom("cu2",customer);
		pr1.start();
		pr2.start();
		cu1.start();
		cu2.start();
	}
}

4.结果

4.1 运行结果:
pr1生产一个,仓库总量:0
pr1生产一个,仓库总量:1
pr1生产一个,仓库总量:2
pr1生产一个,仓库总量:3
pr1生产一个,仓库总量:4
pr1生产一个,仓库总量:5
pr1生产一个,仓库总量:6
pr1生产一个,仓库总量:7
pr1生产一个,仓库总量:8
pr1生产一个,仓库总量:9
仓库已满pr1进入wait状态
cu2消费一个,仓库总量:10
cu2消费一个,仓库总量:9
cu2消费一个,仓库总量:8
cu2消费一个,仓库总量:7
cu2消费一个,仓库总量:6
cu2消费一个,仓库总量:5
cu2消费一个,仓库总量:4
cu2消费一个,仓库总量:3

4.2 结果分析,不出意外的话,我们的程序会一直运行下去,不停的生产,不停的消费。

5. 总结

5.1 wait和notify都是属于object的方法,所以我们任何类都可以去调用这两个方法。

5.2 额外说一下,sleep方法也可以是线程休眠,但是sleep方法是线程独有了,就好比一种是主动,一种是被动,就好比,你在公司上班,老板是线程,你是任务,老板叫你去哪,你就去哪,调用wait方法,是你自己辞职,不想让老板指挥你了,而调用sleep方法,是老板炒你鱿鱼,他不想指挥你了,虽然最种你都不会被别人指挥,结果相同,但过程不一样。sleep不会释放锁,wait会释放锁。你主动辞职了,证明你有能力,想去更好的地方发展,其他老板也都很愿意要你,你可以去其他公司。但是老板炒你鱿鱼,代表你真的不行,其他公司也不愿收留你。列子虽然举得不恰当,但大概是这意思。其实并不复杂,你多看两遍,多理解下,就很容易区分。当然线程还有其他很多方法,这里就不细讲了,主要在于进步,而不要想什么一天就搞懂。以前我有个室友,期末的时候看了两个小时高数书,他说他会了,结果挂了,人家一学期都学不懂的,你又怎么可能短时间学会,后来我们就经常比喻,你以为你是两个小时学完高数哦。扯多了,大概就是这意思。其实我也是个初入互联网的菜鸟,边学习,边写笔记。不要怕自己讲错了,不讲你永远不知道自己对不对,讲出来了,你好歹知道自己哪里不对,该怎么改。希望各位大神指出错误之处或不足之处,一定改之。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值