CPU的时间片十分的珍贵,挂起无用的暂时不用的线程,可以节约系统的资源。
被废弃的suspend
关于挂起的方式我们首先讲一下被废弃的方法,suspend
和 resume
,他们被废弃的原因在于,suspend
使用不当会造成死锁,下面通过一个例子来证明一下。
/**
* 证明 suspend 方法可能造成死锁
*/
public class SuspendDemo implements Runnable {
private Object o = new Object();
@Override
public void run() {
String threadName = Thread.currentThread().getName();
synchronized (o) {
System.out.println(threadName + ">" + "挂起线程");
Thread.currentThread().suspend();
}
System.out.println(threadName + ">" + "已经释放了资源");
}
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new Thread(new SuspendDemo(), "正常线程");
thread1.start();
Thread.sleep(1000);
thread1.resume();
//如果没有延时的sleep resume 方法可能会快于 suspend 方法执行,结果线程被挂起后就不能释放资源造成死锁
Thread deadThread = new Thread(new SuspendDemo(), "死锁线程");
deadThread.start();
deadThread.resume();
}
}
运行结果:
正常线程>挂起线程
正常线程>已经释放了资源
死锁线程>挂起线程
之后你会发现程序一直处在一个卡死的状态。发生了死锁
这是由于 deadThread.start(); 和 deadThread.resume(); 在没有中间Sleep 的状态下,会出现 resume 先于 subspend 方法执行的 的情况,从而导致死锁,所以这个 suspend 方法已经被废弃
wait 和 notify
wait 方法会让线程挂起,但是挂起后会释放资源,从而避免死锁,wait 和 notify 都需要拿到锁才能执行,下面通过一个例子来讲解他们的用法
public class WaitAndNotify implements Runnable {
public static Object o = new Object();
@Override
public void run() {
String name = Thread.currentThread().getName();
synchronized (o) {
System.out.println(name + ">" + "已经获取到锁");
try {
o.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(name + ">" + "被唤醒");
}
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new Thread(new WaitAndNotify(),"线程1");
Thread thread2 = new Thread(new WaitAndNotify(),"线程2");
thread1.start();
thread2.start();
Thread.sleep(3000);
synchronized (o){
o.notify();
}
}
}
同之前的代码类似,线程执行的方法也是获取对象的锁,然后等待唤醒
执行的结果是
线程2>已经获取到锁
线程1>已经获取到锁
线程2>被唤醒
随后未被唤醒的线程一直执行
我们来分析以下,当程序一开始运行的时候,你会看到
线程2>已经获取到锁
线程1>已经获取到锁
这两句话几乎是同时跳出来的,这就说明了wait方法被调用后会释放资源,不然另外一个线程不肯能获取到锁
在sleep 3 秒以后,我们调用notify 方法去随机
唤醒一个线程,注意 notify 和 wait 一样都必须获取锁以后才能去执行。
另外提一下,除了notify 方法以外,还有一个notifyAll,这个方法是将所有的线程都唤醒,让他们去争夺,谁抢到CPU的时间片,谁就执行。