线程进入运行状态之后,可以根据条件触发转为“等待阻塞”:
运行的线程执行wait()方法,该线程会释放占用的所有资源,JVM会把该线程放入“等待池”中。进入这个状态后,是不能自动唤醒的,必须依靠其他线程调用notify()或notifyAll()方法才能被唤醒,
如下代码:
1、定义两个测试线程类
public class WaitTest1 extends Thread {
private Object look;
public WaitTest1(Object look) {
this.look = look;
}
@Override
public void run() {
try {
synchronized (look) {
System.out.println("wait start...");
look.wait();
System.out.println("wait end...");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
public class WaitTest2 extends Thread {
private Object look;
public WaitTest2(Object look) {
this.look = look;
}
@Override
public void run() {
try {
synchronized (look) {
System.out.println("notify start...");
look.notify();
System.out.println("notify end...");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
2、测试类
public class WaitMain {
public static void main(String[] args) {
try {
Object look = "abc";
WaitTest1 thread1 = new WaitTest1(look);
WaitTest2 thread2 = new WaitTest2(look);
thread1.start();
// 两秒后唤醒
Thread.sleep(2000);
thread2.start();
} catch (Exception e) {
e.printStackTrace();
}
}
}
上面代码实现了线程的等待wait和唤醒notify,运行之后输出:
wait start...
wait end...
notify start...
notify end...
说明:
在执行wait()或者notify()之前必须先获得该对象的对象级别锁,即:只能在同步方法或者同步块中调用这两个方法。
关键字synchronized可以将任何一个Object对象作为同步对象来看待,而Java为每一个Object都实现了wait()和notify()方法,
他们必须用在被synchronized同步的Object临界区内。通过调用wait()方法可以使处于临界区内的线程进入等待状态,同时释放被同步对象的锁。
而notify操作可以唤醒一个因调用了wait操作而处于阻塞状态的线程,使其进入就绪状态( 注:是就绪状态,而不是运行状态),被唤醒的线程会试图重新获得临界区的控制权,也就是锁,并继续执行临界区内wait之后的代码。
如果发出notify操作时没有处于阻塞状态的线程,那么该命令会被忽略。
wait()方法可以使调用该方法的线程释放该共享资源的锁,然后从运行状态推出,进入等待队列,直到被再次唤醒。
notify()方法可以随机唤醒等待队列中等待该共享资源的“一个”线程,并使该线程退出等待队列,进入可运行状态,也就是notify()方法仅通知"一个"线程。
notifyAll()方法可以使所有正在等待队列中等待该共享资源的“全部”线程从等待状态退出,进入可运行状态。此时优先级最高的那个线程最先执行,但也有可能是随机执行,因为这要取决于JVM虚拟机的实现。
注意:方法notify()被执行后,不会释放锁,必须执行完notify()方法所在的同步synchronized代码块后才会释放锁,这一点需要注意的。
顺便附上线程状态转换图: