java并发学习--第六章 线程之间的通信

时间:2023-02-14 21:42:08

一、等待通知机制wait()与notify()

  在线程中除了线程同步机制外,还有一个最重要的机制就是线程之间的协调任务。比如说最常见的生产者与消费者模式,很明显如果要实现这个模式,我们需要创建两个线程,一个生产者,一个消费者;有两个线程还不够,如果当生产者生产商品完成后,消费者如何知道要去消费生产的商品?为此JDK给我们可提供了wait()和notify()方法来进行线程之间的通信,从而解决了生产者完成后通知消费者进行消费的问题。

  wait()和notify()是等待/唤醒机制,调用wait()方法可以让当前线程阻塞,只有当使用notify()时,才能让阻塞的线程继续执行。这样的机制就能够达道两个线程之间通信的功能。

  我们来看看这两个方法:

  1.wait()、notify()和notifyAll()方法是都是Objec类中自带的方法,因此所有的类都能够使用;

  2.wait()、notify()和notifyAll()是native方法,无法被重写;

  3.调用某个对象的wait()方法能让当前线程阻塞,并且当前线程必须拥有此对象的锁;

  4.notify()方法能够唤醒一个正在等待的线程,如果有多个线程都在等待被唤醒,调用notify()方法只能随机唤醒一个方法;

  5.调用notifyAll()方法能够唤醒所有正在等待的线程;

  6.使用wait()、notify()和notifyAll()方法必须给线程加锁。

  

  我们用一个例子来介绍生成者与消费者模式:

  生产者代码:

/**
* 生产者,生成了10个商品后通知消费者消费
*/
public class Productor implements Runnable { @Override
public void run() {
//注意,这里生成者和消费者必须拿同一把锁,如果锁不相同,则这个机制无法成功使用
synchronized (Job.value) {
int i = 0;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
while (i < 10) {
//生产任务
Job.value[i] = "商品:" + i;
System.out.println("开始生产" + Job.value[i]);
i++;
}
//生产完成后,唤醒所有线程,让消费者消费
System.out.println("生产完成,开始消费");
Job.value.notifyAll();
}
}
}

  消费者代码:

/**
* 消费者,等待生产者生成完商品后,才开始消费
*/
public class Consume implements Runnable { @Override
public void run() {
//注意,这里生产者和消费者必须拿同一把锁,如果锁不相同,则这个机制无法成功使用
synchronized (Job.value) {
//先判断有没商品生产,如果没有则开始等到生产者生成
if (Job.value[0] == null) {
try {
//等待生产
System.out.println("当前没有任务,等到生产");
Job.value.wait(0);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//生产完成后,唤醒消费者后面的线程任务,消费商品
for (String string : Job.value) {
System.out.println("消费"+string);
}
} }
}

  测试类:

public class Job {

    public static String value[]=new String[10] ;

    public static void main(String[] args) {

        Productor productor=new Productor();

        Consume consume=new Consume();

        new Thread(consume).start();
new Thread(productor).start(); }
}

  运行的结果:

java并发学习--第六章 线程之间的通信

二、ReentrantLock中的等待唤醒机制Condition类

  前面我们已经介绍了JDK中object自带的wait()与notify()方法,但是notify()在多线程环境下只能随机唤醒一个线程,这样的功能实在是有些鸡肋,为此在AQS(AbstractQueuedSynchronizer)中,为我们提供了一个Condition类实现了等待唤醒机制,并且Condition中唤醒的机制更加灵活。

  Condition是在java 1.5中才出现的,它用来替代传统的Object的wait()、notify()实现线程间的协作,相比使用Object的wait()、notify(),使用Condition1的await()、signal()这种方式实现线程间协作更加安全和高效。因此通常来说比较推荐使用Condition。

  Condition中常用的是await()和signal()方法,与wait()与notify一样,必须获取同一把锁才能实现等待唤醒机制,接下来我们就使用Condition中await()和signal()方法对我们的生产者和消费者模式进行改造:再增加一个消费者2,如果商品队列为空,两个消费者都进行等待,当生产者生产完成后唤醒消费者1,消费者1消费完成后唤醒消费者2,这样就能够指定唤醒一个线程

  改造后的生成者:

/**
* 生产者,生成了10个商品后唤醒所有线程
*/
public class Productor implements Runnable {
//获取的锁
Lock lock;
//生成者和消费者1使用的等待/通知对象,用于绑定生成者和消费者1的等待/通知关系
Condition condition; public Productor(Lock lock, Condition condition) {
this.lock = lock;
this.condition = condition;
} @Override
public void run() {
//注意,这里生成者和消费者必须拿同一把锁,如果锁不相同,则这个机制无法成功使用
lock.lock();
int i = 0;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
while (i < 5) {
//生产任务
Job.value[i] = "商品:" + i;
System.out.println("开始生产" + Job.value[i]);
i++;
}
//生产完成后,让消费者1消费
System.out.println("生产完成,开始消费");
condition.signal();
//必须要释放锁
lock.unlock();
}
}

  改造后的消费者1:

/**
* 消费者1,等待生产者生成完商品后,才开始消费
*/
public class Consume implements Runnable {
//获取的锁
Lock lock;
//生成者和消费者1使用的等待/通知对象,用于绑定生成者和消费者1的等待/通知关系
Condition condition;
//消费者1和消费者2使用的等待/通知对象,用于绑定消费者1和消费者2的等待/通知关系
Condition consumeCondition; public Consume(Lock lock, Condition condition, Condition consumeCondition) {
this.lock = lock;
this.condition = condition;
this.consumeCondition = consumeCondition;
} @Override
public void run() {
//注意,这里生产者和消费者必须拿同一把锁,如果锁不相同,则这个机制无法成功使用
lock.lock();
//先判断有没商品生产,如果没有则开始等到生产者生成
if (Job.value[0] == null) {
try {
//等待生产
System.out.println("我是消费者1当前没有任务,等待生产");
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//生产完成后,唤醒消费者后面的线程任务,消费商品
for (String string : Job.value) {
System.out.println("消费者1清理"+string);
}
System.out.println("消费者1清理完毕,唤醒消费者2进行消费");
//当所有都消费完成后,通知消费者2进行消费
consumeCondition.signal();
//必须要释放锁
lock.unlock(); }
}

  新增消费者2:

/**
* 消费者2,等待消费者1清理完成商品后,才开始消费
*/
public class Consume2 implements Runnable {
//获取的锁
Lock lock;
//消费者1和消费者2使用的等待/通知对象,用于绑定消费者1和消费者2的等待/通知关系
Condition consumeCondition; public Consume2(Lock lock, Condition consumeCondition) {
this.lock = lock;
this.consumeCondition = consumeCondition;
} @Override
public void run() {
//注意,这里生产者和消费者必须拿同一把锁,如果锁不相同,则这个机制无法成功使用
lock.lock();
//先判断有没商品生产,如果没有则开始等到生产者生成
if (Job.value[0] == null) {
try {
//等待生产
System.out.println("我是消费者2,当前没有任务,等待生产");
consumeCondition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//生产完成后,唤醒消费者后面的线程任务,消费商品
for (String string : Job.value) {
System.out.println("消费者2消费"+string);
}
//必须要释放锁
lock.unlock(); }
}

  测试类:

public class Job {

    //创建一把锁
private static Lock lock = new ReentrantLock();
//生成者和消费者1使用的等待/通知对象,用于绑定生成者和消费者1的等待/通知关系
private static Condition condition = lock.newCondition();
//消费者1和消费者2使用的等待/通知对象,用于绑定消费者1和消费者2的等待/通知关系
private static Condition consumeCondition = lock.newCondition(); public static String value[] = new String[5]; public static void main(String[] args) { Productor productor = new Productor(lock,condition); Consume consume = new Consume(lock,condition,consumeCondition); Consume2 consume2=new Consume2(lock,consumeCondition);
new Thread(consume2).start();
new Thread(consume).start();
new Thread(productor).start(); }
}

运行的结果:

java并发学习--第六章 线程之间的通信

java并发学习--第六章 线程之间的通信的更多相关文章

  1. java并发学习--第三章 线程安全问题

    线程的安全问题一直是我们在开发过程中重要关注的地方,出现线程安全问题的必须满足两个条件:存在着两个或者两个以上的线程:多个线程共享着共同的一个资源, 而且操作资源的代码有多句.接下来我们来根据JDK自 ...

  2. java并发学习第五章--线程中的锁

    一.公平锁与非公平锁 线程所谓的公平,就是指的是线程是否按照锁的申请顺序来获取锁,如果是遵守顺序来获取,这就是个公平锁,反之为非公平锁. 非公平锁的优点在于吞吐量大,但是由于其不是遵循申请锁的顺序来获 ...

  3. Java并发学习之中的一个——线程的创建

    本文是学习网络上的文章时的总结,感谢大家无私的分享. 1.与每一个Java语言中的元素一样,线程是对象.在Java中,我们有两种方式创建线程: a.通过直接继承thread类,然后覆盖run方法. b ...

  4. java之等待唤醒机制(线程之间的通信)

    线程间通信 概念:多个线程在处理同一个资源,但是处理的动作(线程的任务)却不相同.比如:线程A用来生成包子的,线程B用来吃包子的,包子可以理解为同一资源,线程A与线程B处理的动作,一个是生产,一个是消 ...

  5. java并发学习--第四章 JDK提供的线程原子性操作工具类

    在了解JDK提供的线程原子性操作工具类之前,我们应该先知道什么是原子性:在多线程并发的条件下,对于变量的操作是线程安全的,不会受到其他线程的干扰.接下来我们就学习JDK中线程的原子性操作. 一.CAS ...

  6. java并发学习--第七章 JDK提供的线程工具类

    一.ThreadLocal ThreadLocal类用于隔离多线程中使用的对象,为ThreadLocal类中传递的泛型就是要隔离的对象,简单的来说:如果我们在主线程创建了一个对象,并且需要给下面的多线 ...

  7. Java并发学习之十九——线程同步工具之Phaser

    本文是学习网络上的文章时的总结.感谢大家无私的分享. JDK 1.7 加入了一个新的工具Phaser.Phaser的在功能上与CountDownLatch有部分重合. 以下使用Phaser类来同步3个 ...

  8. JAVA多线程学习八-多个线程之间共享数据的方式

    多个线程访问共享对象和数据的方式 如果每个线程执行的代码相同,可以使用同一个Runnable对象,这个Runnable对象中有那个共享数据,例如,买票系统就可以这么做. 如果每个线程执行的代码不同,这 ...

  9. java并发编程(十一)线程间的通信notify通知的遗漏

    notify通知的遗漏很容易理解,即threadA还没开始wait的时候,threadB已经notify了,这样,threadB通知是没有任何响应的,当threadB退出synchronized代码块 ...

随机推荐

  1. 谷歌的网页排序算法(PageRank Algorithm)

    本文将介绍谷歌的网页排序算法(PageRank Algorithm),以及它如何从250亿份网页中捞到与你的搜索条件匹配的结果.它的匹配效果如此之好,以至于“谷歌”(google)今天已经成为一个被广 ...

  2. AFNetworking教程

    转:http://www.lanrenios.com/tutorials/network/2012/1126/527.html AFNETWORKING AFNetworking他是一个现在非常用得多 ...

  3. AD15高版软件卡不卡,问题解决大讨论

    AD高版软件很卡(包括13 14 15版),这是我遇到过的问题,大家都遇到过的问题, 这里我分享一个解决办法:也请给位有什么好的方法也一起分享. 问题1卡:打开AD15软件, 按住鼠标中键 放大 或 ...

  4. OOP设计模式&lbrack;JAVA&rsqb;——02观察者模式

    观察者模式 观察者模式的设计原则 为交互对象之间的松耦合设计而努力,使对象之间的相互依赖降到最低. 观察者模式也是对象行为型模式,其意图为:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时 ...

  5. textarea出现多余的空格

    今天使用textarea标签,调用数据的时候,出现一些多余的空格,如何改变属性都不能够经过某属性将空格去掉,经过查询,看了zuyi532的专栏(http://blog.csdn.net/zuyi532 ...

  6. viminfo&colon; 无效的启动字符

    当自己进入一个用户,使用vi打开一个文件时,出现以下情况: [gexd@localhost ~]$ vi test.c E575: viminfo: 无效的启动字符 位于行: int main() . ...

  7. php使用curl设置超时的重要性

    原文:http://phpquan.com/lamp/php/php-curl-timeout/ 网站登录不了,原因是没有可用的 PHP 子进程来响应新的请求了.这可能是是由于PHP-curl  没有 ...

  8. 081、Weave Scope 多主机监控(2019-04-29 周一)

    参考https://www.cnblogs.com/CloudMan6/p/7674011.html   Weave Scope 除了监控容器,还可以监控Docker Host.   点击顶部 HOS ...

  9. 基于ip的虚拟主机配置——在一台服务器上绑定多个 IP 地址

    进入/etc/sysconfig/network-scripts,修改ifcfg-ens33文件 输入 ip addr 查看ip 引用:https://blog.csdn.net/u013887008 ...

  10. 2018 pycharm最近激活码

    今天更新了一下pycharm,结果之前的激活就不能用了,下面是新的激活方法: 1.mac下在终端进入etc目录: cd /etc 2.编辑hosts文件: vi hosts 将“0.0.0.0 acc ...