专栏首页Java后端技术栈Java多线程编程-(5)-线程间通信机制的介绍与使用(温馨提示:图文较多,建议Wiff下打开)

Java多线程编程-(5)-线程间通信机制的介绍与使用(温馨提示:图文较多,建议Wiff下打开)

线程间通信简介

我们知道线程是操作系统中独立的个体,但是这个单独的个体之间没有一种特殊的处理方式使之成为一个整体,线程之间没有任何交流和沟通的话,他就是一个个单独的个体,不足以形成一个强大的交互性较强的整体。

为了提高CPU的利用率和各线程之间相互协作,Java的一种实现线程间通信的机制是:wait/notify线程间通信,下边就一起学习一下这种线程间的通信机制。

不使用等待/通知机制实现线程间通信

假如,我们不使用下边需要介绍的机制,那我们如何实现两个线程之间的通信哪,下边看一段代码,实现的是两个线程向一个List里填充数据:

MyList代码:

线程A:

线程B:

测试类Test:

执行结果:

可以看出,当List集合中的数据为5个的时候线程B退出,虽然两个线程之间实现了通信,但是代码中我们的线程B是一直执行着while(true)循环的,直到长度为5才终止执行,显然这种方式是很消耗资源的。所以,就需要一种机制能避免上述的操作又能实现多个线程之间的通信,这就是接下来需要学习的“wait/notify线程间通信”

什么是等待/通知机制

道理很简单,就像我们去银行办业务,进门之后取票号,等到达的时候会广播通知我们办业务一样,这就是很实际的一个场景,我们取了票号就需要等待,等业务员轮到票号的时候就会广播通知。

Java中等待/通知机制的实现

Java中对应等待/通知的方法是wait()/notify(),这两个方法都是超类Object中的方法,如下图所示:

之所以会是超类Object中的方法,我们可以简单的理解:上几篇文章中我们知道任何对象都可以作为锁,而wait()/notify()是由锁调用的,想到这里自然可以体会到这里设计的巧妙之处。

一、wait方法

(1)方法wait()的作用是使当前执行代码的线程进行等待,该方法会将该线程放入”预执行队列“中,并且在wait()所在的代码处停止执行,直到接到通知或被中断为止。

(2)在调用wait()之前,线程必须获得该对象级别锁,这是一个很重要的地方,很多时候我们可能会忘记这一点,即只能在同步方法或同步块中调用wait()方法。

(3)还需要注意的是wait()是释放锁的,即在执行到wait()方法之后,当前线程会释放锁,当从wait()方法返回前,线程与其他线程竞争重新获得锁。

二、notify方法

(1)和wait()方法一样,notify()方法也要在同步块或同步方法中调用,即在调用前,线程也必须获得该对象的对象级别锁。

(2)该方法是用来通知那些可能等待该对象的对象锁的其他线程,如果有多个线程等待,则由线程规划器随机挑选出其中一个呈wait状态的线程,对其发出通知notify,并使它等待获取该对象的对象锁。

(3)这里需要注意的是,执行notify方法之后,当前线程不会立即释放其拥有的该对象锁,而是执行完之后才会释放该对象锁,被通知的线程也不会立即获得对象锁,而是等待notify方法执行完之后,释放了该对象锁,才可以获得该对象锁。

(3)notifyAll()通知所有等待同一共享资源的全部线程从等待状态退出,进入可运行状态,重新竞争获得对象锁。

三、wait()/notify()方法总结

(1)wait()/notify()要集合synchronized关键字一起使用,因为他们都需要首先获取该对象的对象锁;

(2)wait方法是释放锁,notify方法是不释放锁的;

(3)线程的四种状态如下图:

wait/notify线程间通信示例代码

根据上述不使用wait/notify的代码改造如下:

MyList代码:

线程A:

线程B:

测试代码:

运行结果:

上述实例已经实现了简单的等待通知机制,并且我们也可以看到,虽然线程B在第五个元素的时候发出通知,而线程A实现线程B执行完之后才获得对象锁,这也可以说明,wait方法是释放锁的而notify方法是不释放锁的。

另一个案例:使用wait/notify模拟Queue

Queue是队列,我们需要实现的是阻塞的放入和得到数据,设计思路如下:

(1)初始化队列最大长度为5; (2)需要新加入的时候,判断是否长度为5,如果是5则等待插入; (3)需要消费元素的时候,判断是否为0,如果是0则等待消费;

实现代码如下:

执行结果:

其他注意事项

(1)wait()和notify()方法要在同步块或同步方法中调用,即在调用前,线程也必须获得该对象的对象级别锁。

(2)wait方法是释放锁,notify方法是不释放锁的;

(3)notify每次唤醒wait等待状态的线程都是随机的,且每次只唤醒一个;

(4)notifAll每次唤醒wait等待状态的线程使之重新竞争获取对象锁,优先级最高的那个线程会最先执行;

(5)当线程处于wait()状态时,调用线程对象的interrupt()方法会出现InterruptedException异常;

其他知识点

(1)进程间的通信方式:

管道(pipe)、有名管道(named pipe)、信号量(semophore)、消息队列(message queue)、信号(signal)、共享内存(shared memory)、套接字(socket);

(2)线程程间的通信方式:

1、锁机制 1.1 互斥锁:提供了以排它方式阻止数据结构被并发修改的方法。 1.2 读写锁:允许多个线程同时读共享数据,而对写操作互斥。 1.3 条件变量:可以以原子的方式阻塞进程,直到某个特定条件为真为止。

对条件测试是在互斥锁的保护下进行的。条件变量始终与互斥锁一起使用。

2、信号量机制:包括无名线程信号量与有名线程信号量 3、信号机制:类似于进程间的信号处理。

线程间通信的主要目的是用于线程同步,所以线程没有象进程通信中用于数据交换的通信机制。

本文分享自微信公众号 - Java后端技术(JavaITWork),作者:徐刘根

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2017-10-10

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Java多线程编程-(9)-使用线程池实现线程的复用和一些坑的避免

    假设这里有一个系统,大概每秒需要处理5万条数据,这5万条数据为一个批次,而这没秒发送的5万条数据数据需要经过两个处理过程,第一步是数据存入数据库,第二步是对数据...

    Java后端技术
  • Java多线程编程-(6)-你还在使用wait/notify实现进程间的通信吗?

    在《Java多线程编程-(5)-线程间通信机制的介绍与使用》已经学习了,可以使用方法wait/notify 结合同步关键字synchronized实现同步和线程...

    Java后端技术
  • Java多线程编程-(13)-从volatile和synchronized的底层实现原理看Java虚拟机对锁优化所做的努力

    对于Java来说我们知道,Java代码首先会编译成Java字节码,字节码被类加载器加载到JVM里,JVM执行字节码,最终需要转化为汇编指令在CPU上进行执行。

    Java后端技术
  • Android并发编程 多线程与锁

    该文章是一个系列文章,是本人在Android开发的漫漫长途上的一点感想和记录,如果能给各位看官带来一丝启发或者帮助,那真是极好的。

    mafeibiao
  • Android并发编程 多线程与锁

    该文章是一个系列文章,是本人在Android开发的漫漫长途上的一点感想和记录,如果能给各位看官带来一丝启发或者帮助,那真是极好的。

    mafeibiao
  • 【JavaScript】吃饱了撑的系列之JavaScript模拟多线程并发

    最近,明学是一个火热的话题,而我,却也想当那么一回明学家,那就是,把JavaScript和多线程并发这两个八竿子打不找的东西,给硬凑了起来,还写了一个并发库co...

    外婆的彭湖湾
  • java面试必备之ThreadLocal

    按照传统的经验,如果某个对象是非线程安全的,在多线程环境下对象的访问需要采用synchronized进行同步。但是模板类并未采用线程同步机制,因为线程同步会降低...

    JKXQJ
  • Java基础面试题3

    答:方法的重写Overriding和重载Overloading是Java多态性的不同表现。重写Overriding是父类与子类之间多态性的一种表现,重载Over...

    java金融
  • 各个数组底层探索与java并发包

    vector 与 arraylist 的区别?hasmap 和hastable 的区别?

    斯文的程序
  • 一台 Java 服务器可以跑多少个线程?

    打出jstack文件,通过IBM Thread and Monitor Dump Analyzer for Java工具查看如下:

    Java技术栈

扫码关注云+社区

领取腾讯云代金券