Java多线程之生产者消费者模型


线程间的通讯

wait()在对象上等待,等待通知(在等待过程中释放对象锁、等待必须在同步块内、这个对象就是同步锁)《让线程进入阻塞状态,将线程放入等待池中》

notify()通知在这个对象上等待的一个线程,唤醒它,让它不再等下去(必须在同步块内调用,同步锁必须是调用这个方法的对象)《》

notifyAll()和notify()差不多,区别在于是通知在这个对象上等待的所有线程

注意:线程间通讯的几个方法()

总结:wait、notify、notifyAll必须是同步代码块中被调用,

wait()在对象上等待,等待通知(在等待过程中释放对象锁、等待必须在同步块内、这个对象就是同步锁)

//要在这个线程中等待

public class Wifeextends Thread {

private Object message;//等待对象,同时也是同步锁

public Wife(Object message) {

this.message = message;     //初始化,从外部传入

}

public void run(){

synchronized (message){//wait动作必须在一个同步块内部,而且对象锁必须是等待的对象

System.out.println("开始等待");

try {

message.wait();//在对象进行等待,等待过程中释放锁,另一线程的同步块得以执行

System.out.println("收到音讯,结束等待");

} catch(InterruptedException e) {

// TODOAuto-generated catch block

e.printStackTrace();

}

}

}

}

public class Husband{

public static void main(String[] args){

Object message  = new Object();  //创建对象

Wife t = newWife(message);      //新建线程,传入对象

t.start();//启动

System.out.println("上京赶考");

try {

Thread.sleep(5000);

} catch (InterruptedExceptione) {

// TODOAuto-generated catch block

e.printStackTrace();

}

System.out.println("考上状元");

System.out.println("飞鸽传书");

synchronized (message){//notify动作必须在同步块内,而且对象锁必须是调用通知的对象

message.notify();//通知在这个对象上等待的线程,当这个同步块结束,通知的那个线程的wait就返回并继续执行

}

}

}

notify()通知在这个对象上等待的一个线程,唤醒它,让它不再等下去(必须在同步块内调用,同步锁必须是调用这个方法的对象)

notifyAll()和notify()差不多,区别在于是通知在这个对象上等待的所有线程

public class CowManextends Thread {

private Object message;

public CowMan(Object message) {

this.message = message;

}

public void run(){

synchronized (message) {

System.out.println("牛郎等待");

try {

message.wait();

} catch(InterruptedException e) {

// TODOAuto-generated catch block

e.printStackTrace();

}

System.out.println("牛郎去鹊桥");

}

}

}

public class Fairyextends Thread {

private Object message;

public Fairy(Object message) {

this.message = message;

}

public void run(){

synchronized (message) {

System.out.println("织女等待");

try {

message.wait();

} catch(InterruptedException e) {

// TODOAuto-generated catch block

e.printStackTrace();

}

System.out.println("兴冲冲跑去鹊桥相会");

}

}

}

public static voidmain(String[] args) throws Exception {

Object message = newObject();//创建共同使用的信息对象

CowMan cowMan = newCowMan(message);//牛郎和织女都等待同一个信息,七月初七到来的信息

Fairy fairy = newFairy(message);

cowMan.start();

fairy.start();

for(int i=1;i<=7;i++){

System.out.println("七月初"+i);

Thread.sleep(1000);

}

synchronized (message) {

message.notifyAll();//通知在这个对象上等待着的所有线程,牛郎和织女

}

}

1.1    生产者消费者模式

public class Demo {

publicstatic void main(String[] args) {

Resouser = new Resouse("苹果");

newProduce("生产者", r).start();

newConsumer("消费者", r).start();

}

}

/**

* 消费者类

*

*/

class Consumer extends Thread {

Resouser;

publicConsumer(String name, Resouse r) {

super(name);

this.r= r;

}

@Override

publicvoid run() {

//不停拿资源

while(true) {

r.get();

}

}

}

class Produce extends Thread {

Resouser;

publicProduce(String name, Resouse r) {

super(name);

this.r= r;

}

@Override

publicvoid run() {

//不停生产

while(true) {

r.put();

}

}

}

class Resouse {

Stringname;

intid;

booleanflag; // 记录有没有资源

publicResouse(String name) {

this.name= name;

}

/**

* 放资源

*/

publicsynchronized void put() {

try{

if(flag) {

wait();//让线程进入等待状态(阻塞状态的一种),直到被notify()方法唤醒

}

//生产资源

id++;

System.out.println(Thread.currentThread().getName()+ "...生产了....."

+name + id);

flag= true;

//通知消费者

notify();

Thread.sleep(500);

}catch (InterruptedException e) {

//TODO Auto-generated catch block

e.printStackTrace();

}

}

//【同步方法的实质是同步代码块,同一个类中的不同行为<方法>的同步,是可行的。同步锁是当前对象,

//即在多线程中同一时间片段中,对象只会调用类中同步的其中一个同步方法】

/**

* 取资源

*/

public/*synchronized */void get() {

synchronized(this) {//同步锁就是对象监视器,他只会监控同步锁对应的代码块中的代码

try{

if(!flag) { // 没有资源就等待生产

wait();

}

System.out.println(Thread.currentThread().getName()

+"...消费了...." + name + id);

flag= false; // 消费完得告诉生产者没有资源了

//通知生产者

notify();

Thread.sleep(500);

}catch (InterruptedException e) {

//TODO Auto-generated catch block

e.printStackTrace();

}

}

}

}

1.2    生产者消费者复合模式

publicclass Demo {

public static void main(String[] args){

Resouse r = newResouse("苹果");

new Produce("生产者1号",r).start();

new Consumer("消费者1号",r).start();

new Produce("生产者2号", r).start();

new Consumer("消费者2号",r).start();

new Produce("生产者3号",r).start();

}

}

/**

* 消费者类

*

*/

classConsumer extends Thread {

Resouse r;

public Consumer(String name, Resouse r){

super(name);

this.r = r;

}

@Override

public void run() {

// 不停拿资源

while (true) {

r.get();

}

}

}

classProduce extends Thread {

Resouse r;

public Produce(String name, Resouse r){

super(name);

this.r = r;

}

@Override

public void run() {

// 不停生产

while (true) {

r.put();

}

}

}

classResouse {

String name;

int id;

boolean flag; // 记录有没有资源

public Resouse(String name) {

this.name = name;

}

/**

* 放资源

*/

public synchronized void put() {

try {

while (flag) {

wait();// 让线程进入等待状态(阻塞状态的一种),直到被notify()方法唤醒

}

// 生产资源

id++;

System.out.println(Thread.currentThread().getName()+ "...生产了....."

+name + id);

flag = true;

// 通知消费者

notifyAll();

Thread.sleep(500);

} catch (InterruptedExceptione) {

// TODOAuto-generated catch block

e.printStackTrace();

}

}

// 【同步方法的实质是同步代码块,同一个类中的不同行为<方法>的同步,是可行的。同步锁是当前对象,

// 即在多线程中同一时间片段中,对象只会调用类中同步的其中一个同步方法】

/**

* 取资源

*/

public/* synchronized */void get() {

synchronized (this) {//同步锁就是对象监视器,他只会监控同步锁对应的代码块中的代码

try {

while(!flag) { // 没有资源就等待生产

wait();

}

System.out.println(Thread.currentThread().getName()

+"...消费了...."+ name + id);

flag =false; // 消费完得告诉生产者没有资源了

// 通知生产者

notifyAll();

Thread.sleep(500);

} catch(InterruptedException e) {

// TODOAuto-generated catch block

e.printStackTrace();

}

}

}

}

1.3    新的线程同步方法

多生产者多消费者的问题

1、  if语句换成while语句

可以保证在生产或消费之前都先判断一次资源的情况

2、  notity方法换成notifyAll方法

可以保证每次都能唤醒对方的线程

在JDK1.5之后,可以使用Lock接口和Condition接口来解决以上问题

1.3.1  Lock接口

通过Lock接口也可以实现线程的同步处理,并且可以让同步处理变得更加灵活(需要手动实现获取锁和释放锁的操作)

//示例代码

//创建Lock对象(可以将Lock对象理解为原来的锁对象)

Locklock = new ReentrantLock();

lock.lock();//获取锁

try{

//被同步的代码

}finally {

lock.unlock();//释放锁

}

1.3.2  Condition接口

Condition对象将原先在Object类中的监视器方法(wait、notify、notifyAll)抽取出来进行封装,每一个Condition对象都是一个等待池,一个Lock对象可以绑定多个Condition对象,这样一来可以让线程间的通讯操作变得更加灵活。

//创建Condition对象,并和指定的Lock对象绑定

Lock lock = ...;

Condition condition = lock.newCondition();

线程间通讯的三个方法

1)await():替代wait方法

2)signal():替代notify方法

3)signalAll():替代notifyAll方法

1.3.3  示例1

/*

* 有5辆火车要过山洞,但确保山洞同时只能有一辆火车通过(过山洞需要1秒),打印输出火车通过的顺序。

* (过山洞的顺序是不可控的,只要保证同一时间只有一辆火车能通过山洞即可)

* 提示:使用线程同步,一辆火车就是一个线程

*/

publicclass LockDemo {

public static void main(String[] args){

// TODO Auto-generated methodstub

newTrain("火车1").start();

new Train("火车2").start();

new Train("火车3").start();

new Train("火车4").start();

new Train("火车5").start();

}

}

classTrain extends Thread{

//创建Lock对象

static Lock lock = new ReentrantLock();

public Train(String name){

super(name);

}

@Override

public void run() {

//获取锁

lock.lock();

try {

System.out.println(getName()+"过山洞.....");

Thread.sleep(1000);

} catch (InterruptedExceptione) {

// TODOAuto-generated catch block

e.printStackTrace();

}finally{

//释放锁

lock.unlock();

}

}

}

1.3.4  新线程同步复合应用

publicclass Demo {

public static void main(String[] args){

// TODO Auto-generated methodstub

Resource r = newResource("苹果");

new Producer("生产者1号",r).start();

new Producer("生产者2号",r).start();

new Consumer("消费者1号",r).start();

new Consumer("消费者2号",r).start();

new Consumer("消费者三号", r).start();

}

}

/*

* 消费者类

*/

classConsumer extends Thread {

Resource r;

public Consumer(String name, Resourcer) {

super(name);

this.r = r;

}

@Override

public void run() {

while (true) {

r.get();

}

}

}

/*

* 生产者类

*/

classProducer extends Thread {

Resource r;

public Producer(String name, Resourcer) {

super(name);

this.r = r;

}

@Override

public void run() {

while (true) {

r.put();

}

}

}

/*

* 资源类

*/

classResource {

String name;

int id;

boolean flag; // 记录有没有资源

// Lock对象,用于替代同步代码块

Lock lock = new ReentrantLock();

// 创建两个等待池

// 生产者线程用的等待池

Condition producerCon =lock.newCondition();

// 消费者线程用的等待池

Condition consumCon =lock.newCondition();

public Resource(String name) {

this.name = name;

}

// 放资源

public void put() {

lock.lock();

try {

while (flag) {

// 将当前线程(生产者线程放入等待池)

producerCon.await();// 替代了wait方法

}

// 生产资源

id++;

System.out.println(Thread.currentThread().getName()+ "...生产了...."

+name + id);

flag = true;

// 通知消费者消费

// 唤醒消费者等待池中的线程

consumCon.signal();

Thread.sleep(500);

} catch (InterruptedExceptione) {

// TODO Auto-generatedcatch block

e.printStackTrace();

} finally {

lock.unlock();

}

}

public void get() {

lock.lock();

try {

while (!flag) {

consumCon.await();

}

// 消费资源

System.out.println(Thread.currentThread().getName()+ "....消费了...."

+name + id);

flag = false;

//通知生产者生产

producerCon.signal();

Thread.sleep(500);

} catch (InterruptedExceptione) {

// TODOAuto-generated catch block

e.printStackTrace();

}finally{

lock.unlock();

}

}

}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,457评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,837评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,696评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,183评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,057评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,105评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,520评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,211评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,482评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,574评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,353评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,897评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,489评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,683评论 2 335

推荐阅读更多精彩内容

  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,497评论 18 399
  • 文章来源:http://www.54tianzhisheng.cn/2017/06/04/Java-Thread/...
    beneke阅读 1,461评论 0 1
  • 本文主要讲了java中多线程的使用方法、线程同步、线程数据传递、线程状态及相应的一些线程函数用法、概述等。 首先讲...
    李欣阳阅读 2,426评论 1 15
  • Java多线程学习 [-] 一扩展javalangThread类 二实现javalangRunnable接口 三T...
    影驰阅读 2,943评论 1 18
  • 01. 有个朋友毕业之后一直在家,整天也没闲着,帮着家里干这干那。他的父母一直不满意,觉得大学毕业还在家没工作,抬...
    自在娇莺恰恰啼V阅读 283评论 0 2