suspend和resume的废除
查阅java api 可以发现suspend和resume已经被标注为废弃方法。原因其实也不是很复杂,因为suspend在导致线程暂停的同时不会去释放任何资源,直到在对应线程上执行了resume操作,被挂起的线程才能继续,从而其他所有阻塞在相关锁的线程也可以继续执行,但是,如果resume操作意外的在suspend操作前就执行了,那么被挂起的线程很难再去执行。
为了理解suspend的这个问题,可以参考下面这个代码demo
public class BadSuspend {
public static Object u = new Object();
static ChangeObjectThread t1= new ChangeObjectThread("t1");
static ChangeObjectThread t2= new ChangeObjectThread("t2");
public static class ChangeObjectThread extends Thread{
public ChangeObjectThread(String name){
super.setName(name);
}
@Override
public void run(){
synchronized(u){
System.out.println("in "+getName());
Thread.currentThread().suspend();
//ide这里会将suspend方法画上斜线表明已经废弃使用了
}
}
}
public static void main(String[] args) throws InterruptedException {
t1.start();
Thread.sleep(1000);
t2.start();
t1.resume();
t2.resume();
t1.join();
t2.join();
}
}
运行结果:
in t1
in t2
但是要注意一点,此时我们的程序并没有结束运行而是一直处于运行状态!!
现在我们可以借助jps和jstack工具来查看原因哦!
首先笔者用的系统是ubuntu18,当然Windows你就打开cmd就行。下面介绍ubuntu查看方式,windows cmd输入的一样。
打开终端,
输入jps
jet@jet-X555LF:~$ jps
9124 Launcher
9126 BadSuspend
2343 Main
9336 Jps
jet@jet-X555LF:~
找到对应的pid,这里的pid是9126
然后输入jstack 9126
找到这一行
"t2" #11 prio=5 os_prio=0 tid=0x00007f4004209000 nid=0x23cd runnable [0x00007f3ff071c000]
java.lang.Thread.State: RUNNABLE
at java.lang.Thread.suspend0(Native Method)
at java.lang.Thread.suspend(Thread.java:1032)
at BadSuspend$ChangeObjectThread.run(BadSuspend.java:14)
- locked <0x00000000d70ad418> (a java.lang.Object)
这时要注意,当前线程t2是挂起的,但是它的线程状态确实是RUNNABLE,这很有可能使我们误判当前系统状态。同时,虽然主函数中已经调用了resume(),但是由于时间先后顺序的缘故,那个resume并没有生效。
wait()和notify()实现可靠的suspend和resume
public class GoodSuspend {
public static Object u = new Object();
public static class ChangeObjectThread extends Thread {
volatile boolean suspendme = false;
public void suspendMe() {
suspendme = true;
}
public void resumeMe() {
suspendme = false;
synchronized (this) {
notify();
}
}
@Override
public void run() {
while (true) {
synchronized (this) {
while (suspendme) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
synchronized (u) {
System.out.println("in ChangeObjectThread");
}
Thread.yield();
}
}
}
public static class ReadObjectThread extends Thread{
@Override
public void run(){
while(true){
synchronized (u){
System.out.println("in ReadObjectThread");
}
Thread.yield();
}
}
}
public static void main(String[] args) throws InterruptedException {
ChangeObjectThread t1 = new ChangeObjectThread();
ReadObjectThread t2 = new ReadObjectThread();
t1.start();
t2.start();
Thread.sleep(1000);
t1.resumeMe();
System.out.println("suspend t1 2 sec");
Thread.sleep(2000);
System.out.println("resume t1");
t1.resumeMe();
}
}
代码中,给出一个标记变量suspendme,表示当前线程是否被挂起。同时,增加了suspendMe()和resumeMe()两个方法,分别用于挂起线程和继续执行线程。