您的位置:首页 > 编程语言 > Java开发

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

2017-10-10 00:00 435 查看
前几篇:

Java多线程编程-(1)-线程安全和锁Synchronized概念

Java多线程编程-(2)-可重入锁以及Synchronized的其他基本特性

Java多线程编程-(3)-从一个错误的双重校验锁代码谈一下volatile关键字

Java多线程编程-(4)-线程本地ThreadLocal的介绍与使用
线程间通信简介

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

为了提高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、信号机制:类似于进程间的信号处理。

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

注:查看源代码请点击阅读原文,PC端效果更佳!

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐