Java中如何正确的终止线程

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

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

        下面代码模拟一个过程,读线程ReadObjectThread在读到对象的ID和Name不一致时,会输出这些对象;而写线程ChangeObjectThread总是写入连个相同的数值。但是在代码中因为使用了stop()强行停止线程,造成了数据的不同步。

[java]  view plain  copy
  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.                         Thread.sleep(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.             Thread t = new ChangeObjectThread();  
  52.             t.start();  
  53.             Thread.sleep(150);  
  54.                         //使用stop()方法,强制停止线程  
  55.             t.stop();  
  56.         }  
  57.     }  
  58. }  
            User.java的代码
[java]  view plain  copy
  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. }  
          程序的运行结果,出现了数据不一致的情况。
[javascript]  view plain  copy
  1. User [id=1480649515, name=1480649514]  
  2. User [id=1480649516, name=1480649515]  

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

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

   

[java]  view plain  copy
  1. public static class ChangeObjectThread extends Thread {  
  2.   
  3.         // 用于停止线程  
  4.         private boolean stopMe = true;  
  5.   
  6.         public void stopMe() {  
  7.             stopMe = 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.                         Thread.sleep(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,前者不停地睡眠,后者一直运行,然后对这两个线程分别进行中断操作,观察二者的中断标识位。

[java]  view plain  copy
  1. import java.util.concurrent.TimeUnit;  
  2.   
  3. public class Interrupted {  
  4.     public static void main(String[] args) throws Exception {  
  5.         // sleepThread不停的尝试睡眠  
  6.         Thread sleepThread = new Thread(new SleepRunner(), "SleepThread");  
  7.         sleepThread.setDaemon(true);  
  8.         // busyThread不停的运行  
  9.         Thread busyThread = new Thread(new BusyRunner(), "BusyThread");  
  10.         busyThread.setDaemon(true);  
  11.         sleepThread.start();  
  12.         busyThread.start();  
  13.         // 休眠5秒,让sleepThread和busyThread充分运行  
  14.         TimeUnit.SECONDS.sleep(2);  
  15.         sleepThread.interrupt();  
  16.         busyThread.interrupt();  
  17.         System.out.println("SleepThread interrupted is "  
  18.                 + sleepThread.isInterrupted());  
  19.         System.out.println("BusyThread interrupted is "  
  20.                 + busyThread.isInterrupted());  
  21.         // 防止sleepThread和busyThread立刻退出  
  22.         TimeUnit.SECONDS.sleep(2);  
  23.     }  
  24.   
  25.     static class SleepRunner implements Runnable {  
  26.         @Override  
  27.         public void run() {  
  28.             while (true) {  
  29.                 try {  
  30.                     TimeUnit.SECONDS.sleep(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. }  
        程序的运行结果

[javascript]  view plain  copy
  1. SleepThread interrupted is false  
  2. BusyThread interrupted is true  

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

4、使用中断来终止线程

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

        

[java]  view plain  copy
  1. package com.baowei.threadinter;  
  2.   
  3. public class ThreadStopSafeInterrupted {  
  4.     public static void main(String[] args) throws InterruptedException {  
  5.         Thread thread = new Thread() {  
  6.             @Override  
  7.             public void run() {  
  8.                 while (true) {  
  9.                     // 使用中断机制,来终止线程  
  10.                     if (Thread.currentThread().isInterrupted()) {  
  11.                         System.out.println("Interrupted ...");  
  12.                         break;  
  13.                     }  
  14.   
  15.                     try {  
  16.                         Thread.sleep(3000);  
  17.                     } catch (InterruptedException e) {  
  18.                         System.out.println("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.         Thread.sleep(2000);  
  34.         thread.interrupt();  
  35.   
  36.     }  
  37.   
  38. }  

       程序的运行结果

[javascript]  view plain  copy
  1. Interrupted When Sleep ...  
  2. Interrupted ...  

5、关于终止线程的选择

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

      

6、参考的书籍

Java高并发程序设计

Java并发编程的艺术

猜你喜欢

转载自blog.csdn.net/wuqianjing/article/details/80520413