1. 程式人生 > >Java 如何正確停止一個線程

Java 如何正確停止一個線程

str day ble const vol 監聽 mem oracl error

自己在做實驗性小項目的時候,發現自己遇到一個問題:如何控制線程的"死亡"?

首先,如何開啟一個線程呢?

最簡單的代碼:

 1 public class Main {
 2 
 3     public static void main(String[] args) {
 4 
 5         Thread thread = new Thread(new Runnable() {
 6             @Override
 7             public void run() {
 8                 System.out.println("當前線程:" + Thread.currentThread() + ",當前時間戳:" + System.currentTimeMillis());
9 } 10 }); 11 12 thread.start(); 13 14 try { 15 Thread.sleep(1000L); 16 } catch (InterruptedException e) { 17 e.printStackTrace(); 18 } 19 System.out.println("主線程結束"); 20 } 21 }

很簡單,調用.start()方法,這個線程就會啟動.

那麽怎樣主動去停止一個線程呢?要解答這個問題,首先要考慮:為什麽要結束一個線程.

理由如下:

  • 線程是JVM寶貴的資源,有的線程會長時間占用資源.
  • 一些業務邏輯下會出現一個線程從邏輯上完全沒有意義(比如一個定時器,調用了結束的方法),確實需要去停止.

結束一個線程有一個最基本的方法:Thread.stop()方法:

 1 @Deprecated
 2 public final void stop() {
 3     SecurityManager security = System.getSecurityManager();
 4     if (security != null) {
5 checkAccess(); 6 if (this != Thread.currentThread()) { 7 security.checkPermission(SecurityConstants.STOP_THREAD_PERMISSION); 8 } 9 } 10 // A zero status value corresponds to "NEW", it can‘t change to 11 // not-NEW because we hold the lock. 12 if (threadStatus != 0) { 13 resume(); // Wake up thread if it was suspended; no-op otherwise 14 } 15 16 // The VM can handle all thread states 17 stop0(new ThreadDeath()); 18 }

但是這個方法已經是:@Deprecated的了,也就是被建議不要使用的方法.

為什麽不建議使用這個方法的官方說明: http://docs.oracle.com/javase/1.5.0/docs/guide/misc/threadPrimitiveDeprecation.html

實際上,我結合自己經驗提出以下幾點:

  • 線程會直接停掉,按照代碼邏輯要釋放的資源,要調用的接口可能不會被運行(finally塊的代碼還是會執行)
  • 會破壞鎖,導致線程不安全(停掉一個線程就會釋放它持有的鎖,但不能保證邏輯上)

這兩點都是非常嚴重的問題了.即使再小心,去調用stop()也會導致各種各樣的問題.

如果不能"硬"實現結束線程,那麽就可以考慮下"軟"實現.

首先,導致一個線程長時間運行的原因無非有這麽幾個:

  • 代碼邏輯混亂\業務復雜,寫了一個很長的run()方法
  • 長時間睡眠
  • 循環

對於這第一種嘛,只能從代碼結構上優化了.

而這第二種,就要使用Thread.interrupt()方法了.這個方法喚醒睡眠中的線程:

 1 public class Main {
 2 
 3     public static void main(String[] args) {
 4 
 5         Thread thread = new Thread(new Runnable() {
 6             @Override
 7             public void run() {
 8                 try {
 9                     TimeUnit.DAYS.sleep(1L);
10                     System.out.println("睡眠結束");
11                 } catch (Exception e) {
12                     System.out.println("異常:" + e);
13                 } finally {
14                     System.out.println("finally塊被執行");
15                 }
16             }
17         });
18 
19         thread.start();
20 
21         if (!thread.isInterrupted()) {
22             thread.interrupt();
23         }
24         System.out.println("主線程結束");
25     }
26 }

在bio的服務端裏面,會阻塞當前線程.監聽的端口有消息,才會繼續執行,而如果沒有人連接,就需要通過這種方式來打斷正在監聽的線程.

對於這第三種,需要通過輪詢標誌位來控制退出.自己寫的定時器代碼:

  1 public class TimerImpl implements Timer {
  2 
  3     private static final Logger logger = LoggerFactory.getLogger(TimerImpl.class);
  4 
  5     // 定時器線程
  6     private TimerThread timerThread = null;
  7 
  8     private volatile Boolean running = false;
  9 
 10     private Handler taskHandler;
 11 
 12     private Long time;
 13 
 14     private TimeUnit unit;
 15 
 16     @Override
 17     public void start() throws Exception {
 18 
 19         // 給參數生成本地固定拷貝,以確保在運行過程中,不會給參數的改變幹擾
 20         Handler curTask = taskHandler.getClass().newInstance();
 21         Long curTime = new Long(time);
 22         TimeUnit curUnit = unit.getClass().newInstance();
 23 
 24         // 檢查
 25         if (ParameterUtil.checkNull(curTask, curTime, curUnit)) {
 26             throw new Exception("定時器參數配置錯誤");
 27         }
 28 
 29         if (!running) {
 30             synchronized (running) {
 31                 if (!running) {
 32                     timerThread = new TimerThread();
 33 
 34                     timerThread.setTaskHandler(curTask);
 35                     timerThread.setTime(curTime);
 36                     timerThread.setUnit(curUnit);
 37 
 38                     timerThread.start();
 39                     running = true;
 40                 }
 41             }
 42         }
 43     }
 44 
 45     @Override
 46     public void stop() throws Exception {
 47 
 48         if (!running) {
 49             throw new Exception("定時器尚未開始");
 50         }
 51 
 52         synchronized (running) {
 53             if (running) {
 54                 // 標誌位
 55                 timerThread.cancel();
 56                 // 打斷睡眠
 57                 if (!timerThread.isInterrupted()) {
 58                     timerThread.interrupt();
 59                 }
 60                 running = false;
 61             }
 62         }
 63     }
 64 
 65     private class TimerThread extends Thread {
 66 
 67         private volatile boolean stop = false;
 68 
 69         private Handler taskHandler;
 70 
 71         private Long time;
 72 
 73         private TimeUnit unit;
 74 
 75         @Override
 76         public void run() {
 77 
 78             // circle
 79             while (!stop) {
 80 
 81                 // sleep
 82                 try {
 83                     unit.sleep(time);
 84                 } catch (InterruptedException e) {
 85                     logger.info("定時線程被打斷,退出定時任務");
 86                     stop = true;
 87                     return;
 88                 }
 89 
 90                 // do
 91                 try {
 92                     taskHandler.execute();
 93                 } catch (Exception e) {
 94                     logger.error("handler執行異常:{}", this.getClass(), e);
 95                 }
 96             }
 97         }
 98 
 99         public void cancel() {
100             stop = true;
101         }
102 
103         public Handler getTaskHandler() {
104             return taskHandler;
105         }
106 
107         public void setTaskHandler(Handler taskHandler) {
108             this.taskHandler = taskHandler;
109         }
110 
111         public Long getTime() {
112             return time;
113         }
114 
115         public void setTime(Long time) {
116             this.time = time;
117         }
118 
119         public TimeUnit getUnit() {
120             return unit;
121         }
122 
123         public void setUnit(TimeUnit unit) {
124             this.unit = unit;
125         }
126     }
127 
128     @Override
129     public void setTimer(Long time, TimeUnit unit) {
130         this.time = time;
131         this.unit = unit;
132     }
133 
134     @Override
135     public void setHandler(Handler handler) {
136         this.taskHandler = handler;
137     }
138 
139 }

想要停止一個線程的方法是有的,但是會麻煩一些.

Java 如何正確停止一個線程