wait() 和 notify() 顾名思义一个是等待,一个是唤醒。分别表示线程的挂起和唤醒。
两者都是Object类的方法。
wait() 使用的时候必须注意在 synchronized 的代码块里执行。
因为执行 wait 函数之前会先获取到目标对象的监视器,然后释放监视器,方便其他等待在这个目标对象上的线程可以继续执行下去。
package demo1;
public class SimpleWN {
final static Object object = new Object(); //synchronized的目标对象,就是锁
public static class T1 extends Thread {
public void run() {
synchronized(object) {
System.out.println(System.currentTimeMillis()+": T1 start!");
try {
System.out.println(System.currentTimeMillis()+": T1 wait for object!");
object.wait(); //线程挂起,停止执行,并且会把锁释放
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(System.currentTimeMillis()+": T1 end!");
}
}
}
public static class T2 extends Thread {
public void run() {
synchronized(object) {
System.out.println(System.currentTimeMillis()+": T2 start! notify one thread");
object.notify(); //随机唤醒一个等待在object对象上的线程。
System.out.println(System.currentTimeMillis()+": T2 end!");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
public static void main(String[] args) throws InterruptedException {
Thread t1 = new T1();
// Thread t11 = new T1();
Thread t2 = new T2();
t1.start();
// t11.start();
Thread.sleep(2000);
t2.start();
}
}
和notify() 类似的有一个 notifyAll() 函数。
两者有些区别:
-
notify() 会有jvm随机选择一个线程唤醒,这个线程等待锁后就可以执行,其他线程仍旧挂起,除非继续唤醒
-
notifyAll() 会唤醒所有等待在这个锁上的线程,这些线程只要拿到锁后就可以执行
suspend() 和 resume() 是 Thread 类提供的一对用于线程的挂起和唤醒的api。
不过这对api已经被标明不建议使用,原因在于 suspend 挂起线程的时候,线程不会释放任何的锁。如果这个线程不被唤醒,那么将永远的占有锁,导致等待这个锁的其他线程全部执行不下去。
和 wait 的区别:
- wait() 一定要在 synchronized 代码块中使用,因为要获得锁并且释放; suspend 不需要在synchronized 代码块中使用,也不会释放线程占有的锁。
- wait 挂起的线程状态为 WAITING,suspend 挂起的线程状态为 RUNNABLE。这个线程状态对于查找问题的时候是会有误导的。
下面是使用的例子,并且模拟了 suspend 不释放锁的弊端
package demo1;
public class BadSuspend {
public static Object object = new Object();
public static class ChangeObjectThread extends Thread {
public ChangeObjectThread(String name) {
super.setName(name);
}
@Override
public void run() {
synchronized(object) {
System.out.println("in " + getName());
Thread.currentThread().suspend(); //挂起当前线程,但是不会释放锁 object
}
}
}
static ChangeObjectThread t1 = new ChangeObjectThread("t1");
static ChangeObjectThread t2 = new ChangeObjectThread("t2");
public static void main(String[] args) throws InterruptedException {
t1.start();
Thread.sleep(100);
t2.start();
t1.resume(); //唤醒 t1 线程,这个线程 resume 执行的顺序一般在 suspend 后面,因为上面 sleep 了 100 ms.但是这也不是绝对的。两个线程同步执行顺序不可控
t2.resume(); //唤醒 t2 线程, 因为两个线程同步执行,resume 很有可能在线程里的 suspend 之前执行。导致线程错过被唤醒,而 object 锁被一直占用。假设有其它线程等待在这个锁上,那么将永远无法执行
Thread.sleep(1000);
System.out.println(t1.getState().toString());
System.out.println(t2.getState().toString());
t1.join();
t2.join();
}
}
输出结果如下,并且程序无法结束,因为程序挂起后错过了被唤醒的时机
in t1
in t2
TERMINATED
RUNNABLE