线程间通信简介
我们知道线程是操作系统中独立的个体,但是这个单独的个体之间没有一种特殊的处理方式使之成为一个整体,线程之间没有任何交流和沟通的话,他就是一个个单独的个体,不足以形成一个强大的交互性较强的整体。
为了提高CPU的利用率和各线程之间相互协作,Java的一种实现线程间通信的机制是:wait/notify线程间通信,
Java中等待/通知机制的实现
一、wait方法
(1)方法wait()的作用是使当前执行代码的线程进行等待,该方法会将该线程放入”预执行队“中,并且在wait()所在的代码处停止执行,直到接到通知或被中断为止。
(2)在调用wait()之前,线程必须获得该对象级别锁,这是一个很重要的地方,很多时候我们可能会忘记这一点,即只能在同步方法或同步块中调用wait()方法。
(3)还需要注意的是wait()是释放锁的,即在执行到wait()方法之后,当前线程会释放锁,当从wait()方法返回前,线程与其他线程竞争重新获得锁。
二、notify方法
(1)和wait()方法一样,notify()方法也要在同步块或同步方法中调用,即在调用前,线程也必须获得该对象的对象级别锁。
(2)该方法是用来通知那些可能等待该对象的对象锁的其他线程,如果有多个线程等待,则由线程规划器随机挑选出其中一个呈wait状态的线程,对其发出通知notify,并使它等待获取该对象的对象锁。
(3)这里需要注意的是,执行notify方法之后,当前线程不会立即释放其拥有的该对象锁,而是执行完之后才会释放该对象锁,被通知的线程也不会立即获得对象锁,而是等待notify方法执行完之后,释放了该对象锁,才可以获得该对象锁。
(4)notifyAll()通知所有等待同一共享资源的全部线程从等待状态退出,进入可运行状态,重新竞争获得对象锁。
三、wait()/notify()方法总结
(1)wait()/notify()要集合synchronized关键字一起使用,因为他们都需要首先获取该对象的对象锁;
(2)wait方法是释放锁,notify方法是不释放锁的;
(3)线程的四种状态如下图:
其他注意事项
(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、信号机制:类似于进程间的信号处理。
线程间通信的主要目的是用于线程同步,所以线程没有象进程通信中用于数据交换的通信机制。
生产者消费者模型的案例代码:
创建baozi对象:
package com.huanxiao.test10;
public class BaoZi {
String name;
boolean flag;
}
创建吃货类:
package com.huanxiao.test10;
public class ChiHuo extends Thread{
//资源对象
BaoZi baozi;
//定义构造方法,线程定义名字,同时给BaoZi对象赋值
public ChiHuo(String threadName,BaoZi bz){
super(threadName);
this.baozi=bz;
}
/**
* 吃货线程的功能:
* 如果包子不存在 ,线程进入等待状态
* 如果白字存在, 线程开始吃包子,吃完后更改包子的状态为不存在,唤醒早餐店开始制作包子
*
*/
public void run(){
//获取线程名字
String threadName=Thread.currentThread().getName();
for(int i=0;i<10;i++){
synchronized (baozi){
if(baozi.flag){//包子存在
System.out.println(threadName+"正在吃"+baozi.name);//吃包子
baozi.flag=false; //更改包子状态
baozi.notify(); //唤醒同一资源下的其他线程
}else {//包子不存在
try {
baozi.wait();
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
}
}
}
创建早餐店类:
package com.huanxiao.test10;
public class ZaoCanDian extends Thread{
//资源对象
BaoZi baozi;
//定义构造方法,线程定义名字,同时给BaoZi对象赋值
public ZaoCanDian(String threadName,BaoZi bz){
super(threadName);
this.baozi=bz;
}
/**
* 早餐店线程的功能:
* 如果包子不存在 ,线程进入等待状态
* 如果白字存在, 线程开始制作包子,制作完毕后更改包子的状态为存在,唤醒吃货吃包子
*
*/
public void run(){
//获取线程名字
String threadName=Thread.currentThread().getName();
for(int i=0;i<10;i++) {
synchronized (baozi) {
if (baozi.flag) {//包子存在
try {
baozi.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}else {
System.out.println(threadName+"制作"+baozi.name);//制作包子
baozi.flag=true;//更改包子状态
baozi.notify();//唤醒同一资源下的其他线程
}
}
}
}
}
测试类:
package com.huanxiao.test10;
public class ThreadTest {
public static void main(String[] args) {
BaoZi bz=new BaoZi();
bz.name="韭菜鸡蛋";
bz.flag=false;
ChiHuo ch=new ChiHuo("猪八戒",bz);
ZaoCanDian zcd=new ZaoCanDian("春光早餐",bz);
ch.start();
zcd.start();
}
}
结果: