线程之间是可以通信的,线程间通信的方法有wait,notify和notifyAll。这3个方法是定义在Object类里面的,一切类和对象都天然地可以使用者3个方法。但是使用这些方法的前提是在synchronized修饰的语句块下。
wait的意义是中止当前线程,释放锁,并等待;
notify的意义是结束第一个开始等待中的线程,使其处于就绪状态,但是不释放锁。
notifyAll的意义是结束所有正在等待中的线程,全部放入排队获得锁的线程池里面。
用一个例子来说明一下:
import java.util.*; 引入列表类
public class waitandnotify{
private volatile static List list = new ArrayList(); //定义并创建一个列表类对象list
public void add(){ //定义waitandnotify的方法add,这个方法是往对象list里加入一个字符串元素
list.add("Hello");
}
public int size(){ //定义waitandnotify的方法size,这个方法是把对象的size返回
return list.size();
}
public static void main(String[] args){ //main方法
final waitandnotify wt = new waitandnotify(); //创建一个本类的对象
final Object lock = new Object(); //定义并创建一个锁的对象
Thread t1 = new Thread(new Runnable(){ //创建一个线程实例t1
public void run(){ //覆盖t1的run方法
try{
synchronized(lock){ //为run方法的代码块加锁
for(int i=0;i<10;i++){
wt.add(); //调用本类的add方法
/*输出当前线程的名称*/
System.out.println("current thread is "+Thread.currentThread().getName()+" one element added");
Thread.sleep(500); /*中止当前线程500ms,让第二线程执行*/
if(wt.size()==5){ //如果列表list里的元素有5个了,就输出信息,并notify通知wait的线程
System.out.println("message sent");
lock.notify(); //通知wait的排队中的第一个线程
}
}
}
}catch(InterruptedException e){
e.printStackTrace();
}
}
},"t1"); //线程命名为t1
Thread t2 = new Thread(new Runnable(){ //创建第2个线程实例
public void run(){ //覆盖run方法
synchronized(lock){ //给run方法加锁,两个run方法是不同的,但是用的是同一把锁,锁不释放,下面的代码就没法执行
if(wt.size()!=5){ //得到锁之后,当list的元素不是5个时,就执行下面
try{
lock.wait(); //当list里的元素不等于5时,释放锁,并等待
}catch(InterruptedException e){
e.printStackTrace();
}
}
//输出当前线程的名字
System.out.println("Current thread is "+Thread.currentThread().getName()+" message received");
throw new RuntimeException(); //抛一个异常出来
}
}
},"t2");
t2.start(); //先启动t2,先判断线程t2要不要wait
t1.start();
}
}
编译执行,结果如下
由结果可见,线程t2先获得锁,但是不符合条件,释放锁,线程t1得到锁,执行到第5行时,notify线程t1,但是并没有释放锁。一直到线程t1的语句执行完,才释放锁。随后线程t2得到锁,开始执行程序,最后抛一个异常出来。
所以,通过一个例子就能知道,wait和notify的运作机制。关于线程的通信,我相信还有很多很复杂的层面,等我深入学习后,再结合实际的例子来总结。