线程间的通信
JVM在运行时会将自己管理的内存区域,划分为不同的数据区,称为运行时数据区。每个线程都有自己私有的内存空间,如下图示:
Java线程按照自己虚拟机栈中的方法代码一步一步的执行下去,在这一过程中不可避免的会使用到线程共享的内存区域堆或方法区。为了防止多个线程在同一时刻访问同一个内存地址,需要互相告知自己的状态以避免资源争夺。
线程的通信方式主要分为三种方式:①共享内存②消息传递③管道流
共享内存:线程之间通过对共享内存的读-写来实现隐式通信。Java中的具体实现是:volatile共享内存。
消息传递:线程之间通过明确的发送消息来实现显示通信。Java中的具体实现是:等待/通知机制(wait/notify),join方法。
管道流:管道输入/输出流。
1、等待/通知机制
其过程是:线程A由于某些原因,自主调用了对象o的wait方法,进入WAITING状态,释放占有的锁并等待通知。而线程B则调用对象o的notify方法或notifyall方法进行通知,线程A会收到通知,并从wait方法中返回,继续执行后面的代码。
可以发现,线程A和线程B就是通过对象o的wait方法和notify方法来发送消息,进行通信。
wait方法和notify方法是Object类的方法,而Object类是所有类的父类,因此所有对象都实现了Object类的方法。即所有的对象都具有wait方法和notify方法。
由于线程的等待/通知机制需要借助共享对象,所以在调用wait方法前,线程必须先获得该对象的锁,即只能在同步方法或同步块(synchronized代码块)中调用wait方法,在调用wait方法后,线程释放锁。
同样的notify方法在调用前也需要获得对象的锁,即也只能在同步方法或同步块中调用notify方法。若有多个线程在等待,则线程调度器会随机挑选一个线程来通知。需要注意的是,被通知的线程并不会在得到通知后就马上从wait方法返回,而是需要等待获得对象的锁后才能从wait方法返回。而调用了notify方法的线程也并不会在调用时就马上释放对象的锁,而是在执行完同步方法或同步块(synchronized代码块)后,才释放对象的锁。因此,被通知的线程要等调用了notify的线程释放锁后,才能从wait方法中返回。
综上所述,等待/通知机制的经典范式如下:
/**
* 等待线程(调用wait方法的线程)
*/
synchronized(共享对象){ //同步代码块,进入条件是获得锁
while(判断条件){ //进行wait线程任务的条件不满足时进入
共享对象.wait()
}
线程任务代码
}
/**
* 通知线程(调用notify方法的线程)
*/
synchronized(共享对象){ //同步代码块,进入条件是获得锁
线程任务代码
改变wait线程任务的条件
共享对象.notify()
}
根据以上范式,有代码如下:
public class WaitNotify {
static boolean flag = true; //等待线程继续执行往下执行的条件
static Object lock = new Object(); //上锁的对象
public static void main(String[] args) throws InterruptedException {