作者:Bwz_Learning


1、Java中API自带的stop()方法,来终止线程

stop()方法是一个被废弃的方法。为什么stop()方法被废弃而不被使用呢?原因是stop()方法太过于暴力,会强行把执行一半的线程终止。这样会就不会保证线程的资源正确释放,通常是没有给与线程完成资源释放工作的机会,因此会导致程序工作在不确定的状态下。关于使用stop()方法,造成数据不同步的例子如下。

在代码中因为使用了stop()强行停止线程,造成了数据的不同步。

1. public class ThreadStopUnSafe {  
2. public static User user = new User();  
3.   
4. // 改变user变量的线程  
5. public static class ChangeObjectThread extends Thread {  
6. @Override  
7. public void run() {  
8.   
9. while (true) {  
10. synchronized (ThreadStopUnSafe.class) {  
11. int v = (int) (System.currentTimeMillis() / 1000);  
12.                     user.setId(v);  
13. // to do sth  
14. try {  
15. 100);  
16. catch (InterruptedException e) {  
17.                         e.printStackTrace();  
18.                     }  
19.                     user.setName(String.valueOf(v));  
20.                 }  
21. // 让出CPU,给其他线程执行  
22.                 Thread.yield();  
23.             }  
24.   
25.         }  
26.   
27.     }  
28.   
29. // 读取user变量的线程  
30. public static class ReadObjectThread extends Thread {  
31. @Override  
32. public void run() {  
33.   
34. while (true) {  
35. synchronized (ThreadStopUnSafe.class) {  
36. if (user.getId() != Integer.parseInt(user.getName())) {  
37.                         System.out.println(user.toString());  
38.                     }  
39.                 }  
40. // 让出CPU,给其他线程执行  
41.                 Thread.yield();  
42.             }  
43.   
44.         }  
45.     }  
46.   
47. // 测试  
48. public static void main(String[] args) throws InterruptedException {  
49. new ReadObjectThread().start();  
50. while (true) {  
51. new ChangeObjectThread();  
52.             t.start();  
53. 150);  
54. //使用stop()方法,强制停止线程  
55.             t.stop();  
56.         }  
57.     }  
58. }


            User.java的代码

1. public class User {  
2. private int id;  
3. private String name;  
4.   
5. public User() {  
6. this(0, "0");  
7.     }  
8.   
9. public User(int id, String name) {  
10. this.id = id;  
11. this.name = name;  
12.     }  
13.   
14. public int getId() {  
15. return id;  
16.     }  
17.   
18. public void setId(int id) {  
19. this.id = id;  
20.     }  
21.   
22. public String getName() {  
23. return name;  
24.     }  
25.   
26. public void setName(String name) {  
27. this.name = name;  
28.     }  
29.   
30. @Override  
31. public String toString() {  
32. return "User [id=" + id + ", name=" + name + "]";  
33.     }  
34.   
35. }


         程序的运行结果,出现了数据不一致的情况。

1. User [id=1480649515, name=1480649514]  
2. User [id=1480649516, name=1480649515]

2、使用boolean类型的变量,来终止线程

 那么如果需要停止一个线程时,应该怎么办?其实方法很简单,只是需要我们执行确定线程什么时候退出就可以了。仍用本例来说,只需要在ChangeObjectThread线程中增加一个stopMe()方法就可以了。

1. public static class ChangeObjectThread extends Thread {  
2.   
3. // 用于停止线程  
4. private boolean stopMe = true;  
5.   
6. public void stopMe() {  
7. false;  
8.         }  
9.   
10. @Override  
11. public void run() {  
12.   
13. while (stopMe) {  
14.   
15. synchronized (ThreadStopSafeBoolean.class) {  
16. int v = (int) (System.currentTimeMillis() / 1000);  
17.                     user.setId(v);  
18. // to do sth  
19. try {  
20. 100);  
21. catch (InterruptedException e) {  
22.                         e.printStackTrace();  
23.                     }  
24.                     user.setName(String.valueOf(v));  
25.                 }  
26. // 让出CPU,给其他线程执行  
27.                 Thread.yield();  
28.             }  
29.   
30.         }  
31.   
32.     }

           在上面的代码里面,定义了一个标记变量stopMe,用于指示线程是否需要退出。当stopMe()方法被调用时,stopme就会被赋值为false,此时在代码里面的while(stopMe)就会检测到这个改动,线程就退出了。

3、java线程的中断

    中断可以理解为线程的一个标识位属性,它表示一个运行中的线程是否被其他线程进行了中断操作。中断好比其他线程对该线程打了个招呼,其他线程通过调用该线程的interrupt()方法对其进行中断操作。

        线程通过检查自身是否被中断来进行响应,线程通过方法isInterrupted()来进行判断是否被中断,也可以调用静态方法Thread.interrupted()对当前线程的中断标识位进行复位。如果该线程已经处于终结状态,即使该线程被中断过,在调用该线程对象的isInterrupted()时依旧会返回false。

 从Java的API中可以看到,许多声明抛出InterruptedException的方法(例如Thread.sleep(longmillis)方法,当线程在sleep()休眠时,如果被中断,这个异常就会产生)。这些方法在抛出InterruptedException之前,Java虚拟机会先将该线程的中断标识位清除,然后抛出InterruptedException,此时调用isInterrupted()方法将会返回false。

        在代码清单4-7所示的例子中,首先创建了两个线程,SleepThread和BusyThread,前者不停地睡眠,后者一直运行,然后对这两个线程分别进行中断操作,观察二者的中断标识位。

1. import java.util.concurrent.TimeUnit;  
2.   
3. public class Interrupted {  
4. public static void main(String[] args) throws Exception {  
5. // sleepThread不停的尝试睡眠  
6. new Thread(new SleepRunner(), "SleepThread");  
7. true);  
8. // busyThread不停的运行  
9. new Thread(new BusyRunner(), "BusyThread");  
10. true);  
11.         sleepThread.start();  
12.         busyThread.start();  
13. // 休眠5秒,让sleepThread和busyThread充分运行  
14. 2);  
15.         sleepThread.interrupt();  
16.         busyThread.interrupt();  
17. "SleepThread interrupted is "  
18.                 + sleepThread.isInterrupted());  
19. "BusyThread interrupted is "  
20.                 + busyThread.isInterrupted());  
21. // 防止sleepThread和busyThread立刻退出  
22. 2);  
23.     }  
24.   
25. static class SleepRunner implements Runnable {  
26. @Override  
27. public void run() {  
28. while (true) {  
29. try {  
30. 10);  
31. catch (InterruptedException e) {  
32. //e.printStackTrace();  
33.                 }  
34.             }  
35.         }  
36.     }  
37.   
38. static class BusyRunner implements Runnable {  
39. @Override  
40. public void run() {  
41. while (true) {  
42.             }  
43.         }  
44.     }  
45. }

        程序的运行结果

1. SleepThread interrupted is false  
2. BusyThread interrupted is true

        从结果可以看出,抛出InterruptedException的线程SleepThread,其中断标识位被清除了,而一直忙碌运作的线程BusyThread,中断标识位没有被清除。

4、使用中断来终止线程

       下面的例子代码,使用了中断机制来终止一个线程。

1. package com.baowei.threadinter;  
2.   
3. public class ThreadStopSafeInterrupted {  
4. public static void main(String[] args) throws InterruptedException {  
5. new Thread() {  
6. @Override  
7. public void run() {  
8. while (true) {  
9. // 使用中断机制,来终止线程  
10. if (Thread.currentThread().isInterrupted()) {  
11. "Interrupted ...");  
12. break;  
13.                     }  
14.   
15. try {  
16. 3000);  
17. catch (InterruptedException e) {  
18. "Interrupted When Sleep ...");  
19. // Thread.sleep()方法由于中断抛出异常。  
20. // Java虚拟机会先将该线程的中断标识位清除,然后抛出InterruptedException,  
21. // 因为在发生InterruptedException异常的时候,会清除中断标记  
22. // 如果不加处理,那么下一次循环开始的时候,就无法捕获这个异常。  
23. // 故在异常处理中,再次设置中断标记位  
24.                         Thread.currentThread().interrupt();  
25.                     }  
26.   
27.                 }  
28.             }  
29.         };  
30.   
31. // 开启线程  
32.         thread.start();  
33. 2000);  
34.         thread.interrupt();  
35.   
36.     }  
37.   
38. }


       程序的运行结果

1. Interrupted When Sleep ...  
2. Interrupted ...

5、关于终止线程的选择

       感觉使用boolean类型的变量,实现起来比较简单。还不会引起数据的不正确问题。

      

6、参考的书籍

Java高并发程序设计

Java并发编程的艺术