1 前言
前面的文章多线程编程之停止线程的几种方法介绍了停止线程运行的三种方法:
1、使用退出标志,使线程正常退出;
2、使用stop方法强制结束线程,该方法因为不安全已经被废弃;
3、使用interrupt方法中断线程。
在多线程编程中除了通过上述方法停止线程,还可以使用一些方法暂停正在运行中的线程。如果暂停线程可以使用suspend方法,暂停线程意味着可以恢复运行,重启暂停线的线程可以使用resume方法。但是这两个方法和stop方法一样都是过期作废的方法。
2 正文
先通过一个例子看看suspend方法和resume方法的使用:
/** * resume方法和suspend方法的使用 * author:jiangxia * date:2021-04-16 */ public class Demo19 { public static void main(String[] args) throws InterruptedException { Thread19 t = new Thread19(); //使用start方法启动线程 t.start(); Thread.sleep(1000); //该resume方法同样已经被舍弃方法同样已经舍弃 t.suspend();//暂停线程 System.out.println("Time1="+System.currentTimeMillis()+";i="+t.getI()); Thread.sleep(1000); System.out.println("Time1="+System.currentTimeMillis()+";i="+t.getI()); //恢复暂停的线程 //resume方法同样已经被舍弃 t.resume(); Thread.sleep(1000); t.suspend(); System.out.println("Time2="+System.currentTimeMillis()+";i="+t.getI()); Thread.sleep(1000); System.out.println("Time2="+System.currentTimeMillis()+";i="+t.getI()); } } class Thread19 extends Thread{ private long i=0; public long getI() { return i; } public void setI(long i) { this.i = i; } @Override public void run() { while(true){ i++; } } } 复制代码
虽然现在已经成功的暂停和恢复了线程,但是我们会发现 suspend 和 resume 早已被标注为了废弃方法,并且不推荐使用了。IDEA编辑器删除线显示:
那么为什么会被弃用呢?
肯定是在功能上有缺陷。这个缺陷就是不释放锁。如果调用了 suspend 方法的目标线程在挂起时对某一重要的系统资源持有锁,那么在目标线程重新开始之前其他任何线程都无法访问该资源。
/** * resume方法和suspend方法的使用 * author:jiangxia * date:2021-04-16 */ public class Demo20 { public static void main(String[] args) throws InterruptedException { Demo20Service demo20Service = new Demo20Service(); Thread t1 = new Thread(){ @Override public void run() { demo20Service.printInfo(); } }; t1.setName("A"); t1.start(); Thread.sleep(10); //因为A线程暂停了,所以t2线程不运行 Thread t2 = new Thread(){ @Override public void run() { demo20Service.printInfo(); } }; t2.start(); } } class Demo20Service{ //对该方法加synchronized关键字 synchronized public void printInfo(){ System.out.println("线程开始"); //如果线程名为A,暂停线程 if("A".equals(Thread.currentThread().getName())){ System.out.println("A线程suspend"); Thread.currentThread().suspend(); } System.out.println("线程结束"); } } 复制代码
另外还需要注意关于System.out.print()的同步。suspend下println方法不会释放同步锁,程序运行到println方法内部时,同步锁没有被释放,导致当前的PrintStream对象的println方法一直呈『暂停』状态。
/** * suspend的print问题 * author:jiangxia * date:2021-04-16 */ public class Demo21 { public static void main(String[] args) throws InterruptedException { Thread thread = new Demo21Thread(); thread.start(); Thread.sleep(100); thread.suspend(); System.out.println("main线程结束"); } } class Demo21Thread extends Thread{ private long i=0; @Override public void run() { while (true){ i++; System.out.println("i="+i); } } } 复制代码
可以发现结果到最后都不会打印main线程结束。println的源码如下:
public void println(String x) { synchronized (this) { print(x); newLine(); } } 复制代码
因为println方法是一个同步方法,所以线程suspend之后没有释放锁。
另一个缺陷就是不同步。使用 suspend 和 resume 方法也容易出现因为线程的暂停而导致数据不同步的问题。比如:
package com.jiangxia.chap1; /** * suspend和resume导致数据不同步的问题 * author:jiangxia * date:2021-04-16 */ public class Demo22 { public static void main(String[] args) throws InterruptedException { Demo22User user = new Demo22User(); Thread t1 = new Thread(){ @Override public void run() { user.updateUser("貂蝉", "女"); } }; t1.setName("A"); t1.start(); Thread.sleep(10); Thread t2 = new Thread(){ @Override public void run() { user.printUserInfo(); } }; t2.start(); } } class Demo22User { private String name = "吕布"; private String gen = "男"; public void updateUser(String name, String gen){ this.name = name; if ("A".equals(Thread.currentThread().getName())){ System.out.println("停止A线程"); Thread.currentThread().suspend(); } this.gen = gen; } public void printUserInfo(){ System.out.println("姓名=" + name + ", 性别=" +gen); } } 复制代码
可以发现本应输出的是貂蝉女,结果输出的是貂蝉男。程序运行的结果出现了不同步的情况。
3 总结
suspend()方法可以暂停线程,而且不会释放同步锁,而且暂停不会导致睡眠时间的延长;
resume()可以使线程恢复状态,而且会继续执行暂停前的剩余代码。
但是上面两个方法都是过期作废的方法,所以不推荐使用。