1.3 停止线程
在Java中有三种方式可以终止正在运行的线程:
1-1
- 使用退出标志,令线程正常退出,也就是当run方法完成后线程正常终止
-
/*有时候run方法永远不会结束,在下面代码中我们定义了一个exit退出标志*/
/*在定义exit时,使用了关键字volatile,目的是使exit同步,也就是说在同一时刻只能由一个线程来修改exit的值*/
public class ThreadFlag extends Thread { public volatile boolean exit = false; public void run() { while (!exit); } public static void main(String[] args) throws Exception { ThreadFlag thread = new ThreadFlag(); thread.start(); sleep(5000); // 主线程延迟5秒 thread.exit = true; // 终止线程thread thread.join(); System.out.println("线程退出!"); } } - 使用Thread.interrupt()方法中断线程,但是interrupt()方法并不是真正地停止线程,而是在当前线程中打了一个停止的标记。
- 使用Thread.stop()方法强行终止线程,这个方法是不安全的(强制让线程停止有可能使一些清理性工作得不到完成;还有就是对锁定的对象进行“解锁”,导致数据得不到同步的处理,出现数据不一致的问题)。已经被弃用作废了。
1-2
在Java的SDK中,Thread.java类提供了两种方法。
- this.interrupted():测试当前线程是否已经中断,当前线程指的是运行这条指令的线程
- this.isInterrupted():测试线程是否已经中断
public class t4 { public static void main(String[] args){ try{ Mthread thread = new Mthread(); thread.start(); Thread.sleep(1000); thread.interrupt(); System.out.println("是否停止1? = " + thread.interrupted()); //停止的是当前进程,当前进行时main System.out.println("是否停止2? = " + thread.interrupted()); }catch (InterruptedException e){ System.out.println("main catch!"); e.printStackTrace(); } System.out.println("end!"); } } class Mthread extends Thread { @Override public void run() { super.run(); for(int i = 1; i < 50000; i++) { System.out.println("i = " + (i + 1)); } } }
... i = 49993 i = 49994 i = 49995 i = 49996 i = 49997 i = 49998 i = 49999 i = 50000 是否停止1? = false 是否停止2? = false end!
从控制台来看线程并未停止,因为Thread.interrupted()测试的是当前线程是否已经中断,当前线程是main,它从未中断,所以打印出false。
public class t4 { public static void main(String[] args) { System.out.println("当前进程是:" + Thread.currentThread().getName()); Thread.currentThread().interrupt(); System.out.println("是否停止1? = " + Thread.interrupted()); System.out.println("是否停止2? = " + Thread.interrupted()); System.out.println("end!"); } }
当前进程是:main 是否停止1? = true 是否停止2? = false end!
观察上述结果,当前进程main的确被停止了,但是为什么第二个布尔值是false呢?以下是官方文档对interrupted方法的解释:
如果连续两次调用该方法,第一次调用后会清除中断状态,所以第二次输出false
- this.interrupted(): 测试当前线程是否已经是中断状态,并且调用后清除中断状态
- this.isInterrupted():测试线程Thread对象是否已经是中断状态,但不清除中断状态
1-3 用异常法停止线程
由之前的学习我们知道,Thread.interrupt()并不是真正地停止线程,而是清除中断状态,所有我们可以用捕捉异常的方法来停止线程
public class t5 { public static void main(String[] args) { try{ AThread thread = new AThread(); thread.start(); Thread.sleep(2000); thread.interrupt(); }catch(InterruptedException e){ System.out.println("main catch"); e.printStackTrace(); } System.out.println("end!"); } } class AThread extends Thread{ @Override public void run() { super.run(); try { for(int i = 0; i < 50000000; i++) { if(this.interrupted()) { System.out.println("已经是停止状态了!我要退出了!"); throw new InterruptedException(); } System.out.println("i = " + (i + 1)); } System.out.println("我在for循环的下面"); } catch (InterruptedException e) { System.out.println("进入AyThread.java类run方法中的catch了!"); e.printStackTrace(); } } }
i = 770367 i = 770368 i = 770369 i = 770370 i = 770371 i = 770372 i = 770373 i = 770374 i = 770375 i = 770376 i = 770377 i = 770378 i = 770379 end! 已经是停止状态了!我要退出了! 进入AyThread.java类run方法中的catch了! java.lang.InterruptedException at JavaMultiThread.AThread.run(t5.java:26)
当然还可以将方法interrupt()与return结合使用也能实现停止线程的效果。
public class t6 { public static void main(String[] args) throws InterruptedException { XThread thread = new XThread(); thread.start(); Thread.sleep(2000); thread.interrupt(); //System.out.println("end!"); } } class XThread extends Thread{ @Override public void run() { super.run(); while(true) { if(this.isInterrupted()) { System.out.println("停止了!"); return ; } System.out.println("timer = " + System.currentTimeMillis()); } } }
timer = 1602493631139 timer = 1602493631139 timer = 1602493631139 timer = 1602493631139 timer = 1602493631139 timer = 1602493631139 timer = 1602493631139 timer = 1602493631139 timer = 1602493631139 timer = 1602493631139 timer = 1602493631139 timer = 1602493631139 timer = 1602493631139 timer = 1602493631139 timer = 1602493631139 timer = 1602493631139 timer = 1602493631139 timer = 1602493631139 timer = 1602493631139 timer = 1602493631139 timer = 1602493631139 timer = 1602493631139 timer = 1602493631139 timer = 1602493631139 timer = 1602493631139 timer = 1602493631139 停止了!
但是不建议这么做,用异常处理更方便一些,因为catch块还可以将异常向上抛,使得线程停止的事件得以传播。
1-4
package JavaMultiThread; public class t6 { public static void main(String[] args) { XThread thread = new XThread(); thread.start(); //Thread.sleep(200); thread.interrupt(); System.out.println("end!"); } } class XThread extends Thread{ @Override public void run() { super.run(); try { System.out.println("run begin!"); Thread.sleep(200000); System.out.println("run end"); } catch (InterruptedException e) { System.out.println("在沉睡中被停止!进入catch!" + this.isInterrupted()); e.printStackTrace(); } } }
end! run begin! 在沉睡中被停止!进入catch!false java.lang.InterruptedException: sleep interrupted at java.lang.Thread.sleep(Native Method) at JavaMultiThread.XThread.run(t6.java:19)
从结果看,如果在sleep状态下停止某一线程,会进入catch语句,并且要清除状态位,变成false。
1-5 暂停线程
线程暂停以后还可以恢复运行,在Java多线程中,可以使用suspend()和resume()方法回复线程的执行。
package JavaMultiThread; public class t4 { public static void main(String[] args) { try{ Mthread thread = new Mthread(); thread.start(); Thread.sleep(5000); //A段 thread.suspend(); System.out.println("A = " + System.currentTimeMillis() + " i = " + thread.getI()); Thread.sleep(5000); System.out.println("A = " + System.currentTimeMillis() + " i = " + thread.getI()); //B段 thread.resume(); Thread.sleep(5000); //C段 thread.suspend(); System.out.println("B = " + System.currentTimeMillis() + " i = " + thread.getI()); Thread.sleep(5000); System.out.println("B = " + System.currentTimeMillis() + " i = " + thread.getI()); }catch (InterruptedException e){ e.printStackTrace(); } } } class Mthread 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++; } } }
A = 1602505229215 i = 3752119017 A = 1602505234224 i = 3752119017 B = 1602505239227 i = 7510113112 B = 1602505244240 i = 7510113112
可以看到前后两次输出A段的信息相同,这说明线程暂停了;但是B段的信息和A段不同,说明线程又重新恢复了。
1-5-1 suspend与resume方法的缺点-----独占
package JavaMultiThread; public class t4 { public static void main(String[] args) { try{ final SynchronizedObject object = new SynchronizedObject(); Thread thread = new Thread() { @Override public void run() { object.printString(); } }; thread.setName("a"); thread.start(); Thread.sleep(1000); Thread thread1 = new Thread() { @Override public void run() { System.out.println("thread1启动了,但是进入不了printString()方法,只打印1个begin"); System.out.println("因为printString()方法被a线程锁定了并且永远suspend暂停了"); object.printString(); } }; thread1.start(); }catch (InterruptedException e){ e.printStackTrace(); } } } class SynchronizedObject{ synchronized public void printString() { System.out.println("begin!"); if(Thread.currentThread().getName().equals("a")) { System.out.println("进程a永远suspend了!"); Thread.currentThread().suspend(); } System.out.println("end"); } }
begin! 进程a永远suspend了! thread1启动了,但是进入不了printString()方法,只打印1个begin 因为printString()方法被a线程锁定了并且永远suspend暂停了
这说明suspend和resume方法使用不当,造成公共的同步对象被独占,导致其他线程无法访问公共同步对象。现在suspend方法已经作废了
1-5-2 suspend与resume方法的缺点------不同步
package JavaMultiThread; public class t5 { public static void main(String[] args) throws InterruptedException { final MyObject myobject = new MyObject(); Thread thread = new Thread() { @Override public void run() { myobject.setValue("a", "aa"); } }; thread.setName("a"); thread.start(); Thread.sleep(500); Thread thread1 = new Thread() { @Override public void run() { myobject.printUsernamePassword(); } }; thread1.start(); } } class MyObject { private String username = "1"; private String password = "11"; public void setValue(String u, String p) { this.username = u; if(Thread.currentThread().getName().equals("a")) { System.out.println("停止a线程!"); Thread.currentThread().suspend(); } this.password = p; } public void printUsernamePassword() { System.out.println(username + " " + password); } }
停止a线程! a 11
从结果来看,thread线程设置username的值后暂停,并没有给password赋值。thread1读取了此过程中的值,导致读取只赋值到一半的值,出现不同步问题。
从上述两个例子我们可以看出,suspend的resumen方法的缺陷----数据不同步、独占。所以这也是他们过时不被使用的原因。
1.6 yield()方法
yield()方法的作用是放弃当前的CPU资源,让给其他的任务去占用CPU执行时间。当时放弃的时间不确定,有可能刚刚放弃,就马上又获得CPU时间片。
1.7 线程的优先级
在操作系统中,线程可以划分优先级,优先级较高的线程得到的CPU资源较多,也就是说CPU优先执行优先级较高的线程对象中的任务。设置线程优先级有助于“线程规划器”确定下一次选择哪一个线程来执行。设置线程优先级用setPriority()方法。
在Java中,线程的优先级分为1~10折10个等级,如果小于1或者大于10,则JDK会抛出throw new IllegalArgumentException()。默认的优先级是5。
线程的优先级具有一些特性:
①、继承特性:如线程A启动线程B,那么线程B和线程A的优先级是一样的。
package JavaMultiThread; public class t5 { public static void main(String[] args) throws InterruptedException { System.out.println("man thread begin priority = " + Thread.currentThread().getPriority()); Thread.currentThread().setPriority(7); MyThread1 thread1 = new MyThread1(); thread1.start(); System.out.println("man thread end priority = " + Thread.currentThread().getPriority()); } } class MyThread1 extends Thread{ @Override public void run() { super.run(); System.out.println("MyThread1 run priority = " + this.getPriority()); MyThread2 thread = new MyThread2(); thread.start(); } } class MyThread2 extends Thread{ @Override public void run() { super.run(); System.out.println("MyThread2 run priority = " + this.getPriority()); } }
man thread begin priority = 5 man thread end priority = 7 MyThread1 run priority = 7 MyThread2 run priority = 7
②、线程优先级具有规则性
所谓规则性指的是,CPU尽量将执行资源让给优先级较高的线程,所以高优先级的线程总是大部分先执行完,但这不代表高优先级的线程全部先执行完。总之一句话:县城的优先级与代码的执行顺序无关。
package JavaMultiThread; import java.util.Random; public class t5 { public static void main(String[] args) throws InterruptedException { for(int i = 0; i < 10; i++) { MyThread1 thread1 = new MyThread1(); thread1.setPriority(2); thread1.start(); MyThread2 thread2 = new MyThread2(); thread2.setPriority(10); thread2.start(); } } } class MyThread1 extends Thread{ @Override public void run() { super.run(); long beginTime = System.currentTimeMillis(); long addResult = 0; for(int j = 0; j < 10; j++) { for(int i = 0; i < 50000; i++) { Random random = new Random(); random.nextInt(); addResult += i; } } long endTime = System.currentTimeMillis(); System.out.println("++++++++ thread 1 use time = " + (endTime - beginTime)); } } class MyThread2 extends Thread{ @Override public void run() { super.run(); long beginTime = System.currentTimeMillis(); long addResult = 0; for(int j = 0; j < 10; j++) { for(int i = 0; i < 50000; i++) { Random random = new Random(); random.nextInt(); addResult += i; } } long endTime = System.currentTimeMillis(); System.out.println("-------- thread 2 use time = " + (endTime - beginTime)); } }
-------- thread 2 use time = 502 -------- thread 2 use time = 531 -------- thread 2 use time = 682 -------- thread 2 use time = 731 -------- thread 2 use time = 752 -------- thread 2 use time = 786 ++++++++ thread 1 use time = 910 -------- thread 2 use time = 912 -------- thread 2 use time = 929 -------- thread 2 use time = 939 -------- thread 2 use time = 987 ++++++++ thread 1 use time = 1107 ++++++++ thread 1 use time = 1108 ++++++++ thread 1 use time = 1128 ++++++++ thread 1 use time = 1152 ++++++++ thread 1 use time = 1155 ++++++++ thread 1 use time = 1158 ++++++++ thread 1 use time = 922 ++++++++ thread 1 use time = 1170 ++++++++ thread 1 use time = 1172
③、线程优先级具有随机性。
不要把线程的优先级与运行结果的顺序作为衡量的标准,因为线程较高的线程并不一定都先执行完run方法中的任务,也就是线程优先级与打印顺序无关,他们之间的关系具有不确定性和随机性。
1-8 守护线程
Java线程中有两种线程,一种是用户线程,另一种是守护线程。当进程中不存在非守护线程了,守护线程会自动销毁,如垃圾回收器GC就是守护线程。
package JavaMultiThread; public class t6 { public static void main(String[] args) throws InterruptedException { XThread thread = new XThread(); thread.setDaemon(true); thread.start(); Thread.sleep(7000); //让守护线程Daemon Thread多打印一些 System.out.println(Thread.currentThread().getName() +"离开, thread对象也不再打印了,也就是停止了!"); } } class XThread extends Thread{ private int i = 0; @Override public void run() { super.run(); try { while(true) { i++; System.out.println("i = " + (i + 1)); Thread.sleep(1000); } } catch (InterruptedException e) { // TODO 自动生成的 catch 块 e.printStackTrace(); } } }
i = 2 i = 3 i = 4 i = 5 i = 6 i = 7 i = 8 main离开, thread对象也不再打印了,也就是停止了!
作者:Ryanjie
出处:http://www.cnblogs.com/ryanjan/
本文版权归作者和博客园所有,欢迎转载。转载请在留言板处留言给我,且在文章标明原文链接,谢谢!
如果您觉得本篇博文对您有所收获,觉得我还算用心,请点击右下角的 [推荐],谢谢!