Java線程的中斷

引言java

Java沒有提供任何機制來安全地終止線程,但提供了中斷機制,即thread.interrupt()方法。線程中斷是一種協做式的機制,並非說調用了中斷方法以後目標線程必定會當即中斷,而是發送了一箇中斷請求給目標線程,目標線程會自行在某個取消點中斷本身。這種設定頗有必要,由於若是不論線程執行到何種狀況都當即響應中斷的話,很容易形成某些對象狀態不一致的狀況出現。程序員

正文安全

1、中斷相關的方法介紹多線程

涉及到中斷的線程基礎方法有三個:interrupt()、isInterrupted()、interrupted(),它們都位於Thread類下。Thread類下還有一個框架

interrupt()方法:對目標線程發送中斷請求,看其源碼會發現最終是調用了一個本地方法實現的線程中斷;ide

interrupted()方法:返回目標線程是否中斷的布爾值(經過本地方法實現),且返回後會重置中斷狀態爲未中斷;ui

isInterrupted()方法:該方法返回的是線程中斷與否的布爾值(經過本地方法實現),不會重置中斷狀態;spa

2、線程中斷線程

線程中斷能夠按中斷時線程狀態分爲兩類,一類是運行時線程的中斷,一類是阻塞或等待線程的中斷。有中斷時,運行時的線程會在某個取消點中斷執行,而處於阻塞或者等待狀態的線程大多會當即響應中斷,好比上一篇文章中提到的join、sleep等方法,這些方法在拋出中斷異常的錯誤後,會重置線程中斷狀態爲未中斷。但注意,獲取獨佔鎖的阻塞狀態與BIO的阻塞狀態不會響應中斷。而在JUC包中有在加鎖阻塞的過程當中響應中斷的方法,好比lockInterruptibly()。code

下面從三個問題來說述線程中斷

一、線程中斷的目的是什麼?

爲何要進行線程中斷?有時是因爲對於某種特定狀況,咱們知道當前線程無需繼續執行下去,此時能夠中斷此線程;有時是遇到某些異常,須要中斷線程。具體什麼目的,還要看具體場景,但線程中斷的需求已經擺在那裏,確定須要。

二、要如何處理線程中斷?

一般的處理方式有兩種,若是是業務層面的代碼,則只須要作好中斷線程以後的業務邏輯處理便可,而若是是偏底層功能的線程中斷,則儘可能將中斷異常拋出(或者在catch中從新調用interrupt()來中斷線程),以告知上層方法本線程的中斷經歷。

三、JUC中對中斷的處理舉例

JUC中ReentrantLock經常使用的加鎖方法是lock(),還有一個響應中斷的加鎖方法lockInterruptibly()

lock()方法中的acquire(int arg)方法以下所示:

1 public final void acquire(int arg) {
2         if (!tryAcquire(arg) &&
3             acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
4             selfInterrupt();
5 }

在acquireQueued中會對線程的中斷狀態作判斷,若是中斷了則返回true,進入selfInterrupt()方法,恢復線程的中斷狀態。但注意此處是在獲取到鎖以後再響應中斷,在獲取到鎖以前不會作出響應。

1 static void selfInterrupt() {
2         Thread.currentThread().interrupt();
3 }

而看lockInterruptibly()方法:

 1 public void lockInterruptibly() throws InterruptedException {
 2         sync.acquireInterruptibly(1);
 3     }
 4 
 5 public final void acquireInterruptibly(int arg)
 6             throws InterruptedException {
 7         if (Thread.interrupted())
 8             throw new InterruptedException();
 9         if (!tryAcquire(arg))
10             doAcquireInterruptibly(arg);
11     }

它會先查看中斷狀態,再獲取鎖。而若是在獲取鎖的過程當中中斷過,則會在doAcquireInterruptibly方法中拋出中斷異常。

 下面是我在本地模擬的lock阻塞中斷:

 1 public class ReentrantLockDemo {
 2     public static void main(String[] args) throws InterruptedException {
 3         System.out.println("main start");
 4         Thread thread1 = new Thread(new LockThreadDemo());
 5         Thread thread2 = new Thread(new LockThreadDemo());
 6         thread1.start();
 7         Thread.sleep(1000); // 確保thread1獲取到了鎖
 8         thread2.start(); // 此時thread2處於獲取鎖的阻塞狀態
 9         thread2.interrupt();
10         System.out.println("main end");
11     }
12 }
13 
14 class LockThreadDemo implements Runnable {
15     public static ReentrantLock lock = new ReentrantLock();
16     @Override
17     public void run() {
18         System.out.println(Thread.currentThread().getName() + "runnable run");
19         try {
20             lock.lock();
21             System.out.println(Thread.currentThread().getName() + "開始睡眠");
22             Thread.sleep(5000);
23             System.out.println(Thread.currentThread().getName() + "睡了5秒");
24         } catch (Exception e) {
25             System.out.println(Thread.currentThread().getName() + "runnable exception:" + e);
26         } finally {
27             lock.unlock();
28         }
29         System.out.println(Thread.currentThread().getName() + " over");
30     }
31 }

執行結果爲:

main start
Thread-0runnable run
Thread-0開始睡眠
main end
Thread-1runnable run
Thread-0睡了5秒
Thread-0 over
Thread-1開始睡眠
Thread-1runnable exception:java.lang.InterruptedException: sleep interrupted
Thread-1 over

能夠看到中斷了並無對獲取鎖產生影響,最後是sleep方法響應的中斷。

下面是我在本地模擬的lockInterruptibly()阻塞中斷:

 1 public class ReentrantLockInterruptableDemo {
 2     public static void main(String[] args) throws InterruptedException {
 3         System.out.println("main start");
 4         Thread thread1 = new Thread(new LockThreadInterruptableDemo());
 5         Thread thread2 = new Thread(new LockThreadInterruptableDemo());
 6         thread1.start();
 7         Thread.sleep(1000); // 確保thread1獲取到了鎖
 8         thread2.start(); // 此時thread2處於獲取鎖的阻塞狀態
 9         thread2.interrupt();
10         System.out.println("main end");
11     }
12 }
13 
14 class LockThreadInterruptableDemo implements Runnable {
15     public static ReentrantLock lock = new ReentrantLock();
16     @Override
17     public void run() {
18         System.out.println(Thread.currentThread().getName() + "runnable run");
19         try {
20             lock.lockInterruptibly();
21             System.out.println(Thread.currentThread().getName() + "開始睡眠");
22             Thread.sleep(5000);
23             System.out.println(Thread.currentThread().getName() + "睡了5秒");
24         } catch (Exception e) {
25             System.out.println(Thread.currentThread().getName() + "runnable exception:" + e);
26         } finally {
27             try {
28                 lock.unlock();
29             } catch (IllegalMonitorStateException e) {
30                 System.out.println("因線程" + Thread.currentThread().getName() + "提早中斷致使未獲取到鎖");
31             }
32         }
33         System.out.println(Thread.currentThread().getName() + " over");
34     }
35 }

結果爲:

main start
Thread-0runnable run
Thread-0開始睡眠
main end
Thread-1runnable run
Thread-1runnable exception:java.lang.InterruptedException
因線程Thread-1提早中斷致使未獲取到鎖
Thread-1 over
Thread-0睡了5秒
Thread-0 over

結束語

對於線程中斷的處理比較常見,尤爲是涉及到多線程的框架、組件中。而可否處理好線程中斷的各類狀況,則體現出一個程序員對多線程掌握的熟練狀況。天天進步一點,日拱一卒,加油!

相關文章
相關標籤/搜索