前言
本文主要集中在
Java線(xiàn)程的中斷的使用和操作上.
完整代碼:代碼
方法
關(guān)于中斷的有三個(gè)方法都在
java.lang.thread下,分別如下:
| 方法 | 描述 |
|---|---|
public boolean isInterrupted() |
判斷某個(gè)線(xiàn)程是否中斷 |
public static boolean interrupted() |
判斷當(dāng)前線(xiàn)程是否中斷,與isInterrupted()的區(qū)別在于如果當(dāng)前線(xiàn)程中斷了,該方法在返回true后,會(huì)進(jìn)行復(fù)位(即把中斷標(biāo)識(shí)符設(shè)置為false) |
public void interrupt() |
中斷某個(gè)線(xiàn)程 |
再看一個(gè)例子之前先注意一點(diǎn):
1.如果某個(gè)線(xiàn)程已經(jīng)處于終結(jié)狀態(tài),即使該線(xiàn)程被中斷過(guò),在調(diào)用該線(xiàn)程對(duì)象的
isInterrupted()方法會(huì)依舊返回false
例子1
通過(guò)該例子簡(jiǎn)單理解一下中斷的操作
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
public class Test1 {
static CountDownLatch countdown = new CountDownLatch(1);
//countdown 是為了保證mythread-1充分運(yùn)行完
public static void main(String[] args) {
Thread thread = new Thread(new Runner1(), "mythread-1");
thread.start();
try {
TimeUnit.NANOSECONDS.sleep(1);
thread.interrupt();
TimeUnit.NANOSECONDS.sleep(1);
thread.interrupt();
countdown.await();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
printThreadInfo(Thread.currentThread());
printThreadInfo(thread);
System.out.println("main ends!");
}
static void printThreadInfo(Thread thread) {
System.out.println(thread.getName() + ", interrupte :" + thread.isInterrupted());
}
static class Runner1 implements Runnable {
public void run() {
System.out.println("Thread-name:" + Thread.currentThread().getName() + ", interrupted:" + Thread.currentThread().isInterrupted());
int i = 0;
while(true) {
if (Thread.interrupted()) {
System.out.println(Thread.currentThread().getName() + " receives interrupt signal in first loop");
break;
}
System.out.println(Thread.currentThread().getName() + " prints " + (i++) + " in first loop.");
}
System.out.println(Thread.currentThread().getName() + " does not stop!");
printThreadInfo(Thread.currentThread());
while (true) {
if (Thread.currentThread().isInterrupted()) {
System.out.println(Thread.currentThread().getName() + " receives interrupt signal in second loop");
break;
}
System.out.println(Thread.currentThread().getName() + " prints " + (i++) + " in second loop.");
}
printThreadInfo(Thread.currentThread());
countdown.countDown();
}
}
}
首先定義了一個(gè)
CountDownLatch為了保證讓mythread-1充分運(yùn)行后在運(yùn)main線(xiàn)程await()后面的語(yǔ)句.
線(xiàn)程
mythread-1有兩個(gè)循環(huán),第一個(gè)用Thread.interrupted()來(lái)判斷當(dāng)前線(xiàn)程是否中斷,如果有中斷,它會(huì)進(jìn)行復(fù)位.第二個(gè)循環(huán)使用isInterrupted()來(lái)判斷當(dāng)前線(xiàn)程是否有中斷.
主線(xiàn)程
main中調(diào)用了兩次interrupt()中斷操作來(lái)告訴mythread-1.
我的電腦上運(yùn)行結(jié)果如下:
Thread-name:mythread-1, interrupted:false
mythread-1 prints 0 in first loop.
mythread-1 prints 1 in first loop.
mythread-1 prints 2 in first loop.
mythread-1 prints 3 in first loop.
mythread-1 prints 4 in first loop.
mythread-1 prints 5 in first loop.
mythread-1 prints 6 in first loop.
mythread-1 prints 7 in first loop.
mythread-1 prints 8 in first loop.
mythread-1 prints 9 in first loop.
mythread-1 prints 10 in first loop.
mythread-1 prints 11 in first loop.
mythread-1 prints 12 in first loop.
mythread-1 prints 13 in first loop.
mythread-1 prints 14 in first loop.
mythread-1 prints 15 in first loop.
mythread-1 prints 16 in first loop.
mythread-1 prints 17 in first loop.
mythread-1 prints 18 in first loop.
mythread-1 prints 19 in first loop.
mythread-1 prints 20 in first loop.
mythread-1 prints 21 in first loop.
mythread-1 prints 22 in first loop.
mythread-1 prints 23 in first loop.
mythread-1 prints 24 in first loop.
mythread-1 prints 25 in first loop.
mythread-1 prints 26 in first loop.
mythread-1 prints 27 in first loop.
mythread-1 prints 28 in first loop.
mythread-1 prints 29 in first loop.
mythread-1 prints 30 in first loop.
mythread-1 prints 31 in first loop.
mythread-1 prints 32 in first loop.
mythread-1 prints 33 in first loop.
mythread-1 prints 34 in first loop.
mythread-1 prints 35 in first loop.
mythread-1 prints 36 in first loop.
mythread-1 prints 37 in first loop.
mythread-1 prints 38 in first loop.
mythread-1 prints 39 in first loop.
mythread-1 prints 40 in first loop.
mythread-1 prints 41 in first loop.
mythread-1 prints 42 in first loop.
mythread-1 prints 43 in first loop.
mythread-1 receives interrupt signal in first loop
mythread-1 does not stop!
mythread-1, interrupte :false
mythread-1 prints 44 in second loop.
mythread-1 prints 45 in second loop.
mythread-1 prints 46 in second loop.
mythread-1 prints 47 in second loop.
mythread-1 prints 48 in second loop.
mythread-1 prints 49 in second loop.
mythread-1 prints 50 in second loop.
mythread-1 prints 51 in second loop.
mythread-1 prints 52 in second loop.
mythread-1 prints 53 in second loop.
mythread-1 prints 54 in second loop.
mythread-1 prints 55 in second loop.
mythread-1 prints 56 in second loop.
mythread-1 prints 57 in second loop.
mythread-1 prints 58 in second loop.
mythread-1 prints 59 in second loop.
mythread-1 prints 60 in second loop.
mythread-1 prints 61 in second loop.
mythread-1 prints 62 in second loop.
mythread-1 prints 63 in second loop.
mythread-1 prints 64 in second loop.
mythread-1 prints 65 in second loop.
mythread-1 prints 66 in second loop.
mythread-1 prints 67 in second loop.
mythread-1 receives interrupt signal in second loop
mythread-1, interrupte :true
main, interrupte :false
mythread-1, interrupte :false
main ends!
從結(jié)果中可以看到主線(xiàn)程第一次調(diào)用
thread.interrupt();的時(shí)候會(huì)mythread-1線(xiàn)程中的第一個(gè)循環(huán)利用Thread.interrupted()會(huì)檢查到中斷狀態(tài)已經(jīng)判斷為true然后會(huì)立馬復(fù)位為false,從跳出第一個(gè)循環(huán)后打印的中斷狀態(tài)信息就可以知道線(xiàn)程狀態(tài)已經(jīng)被復(fù)位為false了.
主線(xiàn)程第二次調(diào)用
thread.interrupt();的時(shí)候會(huì)mythread-1線(xiàn)程中的第二個(gè)循環(huán)利用Thread.currentThread().isInterrupted()會(huì)檢查到中斷狀態(tài)會(huì)判斷為true,跳出循環(huán)后會(huì)發(fā)現(xiàn)這次并沒(méi)有復(fù)位.
所以從這個(gè)例子中就可以明顯區(qū)分interrupted()和isInterrupted()了.
最后在主線(xiàn)程打印
mythread-1的中斷狀態(tài)信息,返回的是false已經(jīng)該線(xiàn)程已經(jīng)處于終結(jié)狀態(tài)了.
不過(guò)從這個(gè)例子中也可以看到幾點(diǎn)疑惑:
1. 線(xiàn)程收到線(xiàn)程中斷信息后并沒(méi)有立刻停止線(xiàn)程,而是繼續(xù)運(yùn)行.
2.InterruptedException是什么?有什么用?
中斷的理解
中斷可以理解為線(xiàn)程的一個(gè)標(biāo)識(shí)位屬性,它表示一個(gè)運(yùn)行中的線(xiàn)程是否被其他線(xiàn)程進(jìn)行了中斷操作也就是該線(xiàn)程的中斷狀態(tài).
下面一段話(huà)我覺(jué)得寫(xiě)得很好,摘錄于線(xiàn)程中斷
中斷是一種協(xié)作機(jī)制。當(dāng)一個(gè)線(xiàn)程中斷另一個(gè)線(xiàn)程時(shí),被中斷的線(xiàn)程不一定要立即停止正在做的事
情。
相反,中斷是禮貌地請(qǐng)求另一個(gè)線(xiàn)程在它愿意并且方便的時(shí)候停止它正在做的事情。有些方法,
例如 Thread.sleep(),很認(rèn)真地對(duì)待這樣的請(qǐng)求,但每個(gè)方法不是一定要對(duì)中斷作出響應(yīng)。
對(duì)于中斷請(qǐng)求,不阻塞但是仍然要花較長(zhǎng)時(shí)間執(zhí)行的方法可以輪詢(xún)中斷狀態(tài),并在被中斷的時(shí)候
提前返回。 您可以隨意忽略中斷請(qǐng)求,但是這樣做的話(huà)會(huì)影響響應(yīng)。
因?yàn)檫@樣可以帶來(lái)靈活性,用戶(hù)可以根據(jù)自己的需求來(lái)決定是否進(jìn)行響應(yīng).
當(dāng)另一個(gè)線(xiàn)程通過(guò)調(diào)用 Thread.interrupt() 中斷一個(gè)線(xiàn)程時(shí),會(huì)出現(xiàn)以下兩種情況之一
1. 那個(gè)線(xiàn)程在執(zhí)行一個(gè)低級(jí)可中斷阻塞方法,例如 Thread.sleep()、 Thread.join() 或 Object.wait(),那么它將取消阻塞并拋出 InterruptedException. 因?yàn)檫@些阻塞方法,由于無(wú)法預(yù)測(cè)什么時(shí)間會(huì)結(jié)束,因此需要一種中斷機(jī)制來(lái)進(jìn)行通知該線(xiàn)程的調(diào)用者,讓調(diào)用者明白該線(xiàn)程已經(jīng)發(fā)生中斷并且提前返回.
注意:這些方法會(huì)在異常拋出的時(shí)候會(huì)清除當(dāng)前線(xiàn)程的中斷狀態(tài)拿wait()方法的源碼看一下就可以了
The <i>interrupted status</i> of the current thread is cleared when this exception is thrown.
2. interrupt() 只是設(shè)置線(xiàn)程的中斷狀態(tài).
例子2
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
public class Test2 {
public static void main(String[] args) {
Object obj = new Object();
Thread thread = new Thread(new Runner2(obj), "mythread-1");
thread.start();
try {
TimeUnit.MILLISECONDS.sleep(1);
thread.interrupt();
TimeUnit.MILLISECONDS.sleep(1);
thread.interrupt();
} catch (Exception e) {
e.printStackTrace();
}
}
static void printThreadInfo(String info, Thread thread) {
System.out.println(info + thread.getName() + ", interrupte :" + thread.isInterrupted());
}
static class Runner2 implements Runnable {
Object obj;
public Runner2(Object obj) {
this.obj = obj;
}
public void run() {
synchronized(obj) {
try {
System.out.println(Thread.currentThread().getName() + " before waits");
obj.wait();
System.out.println(Thread.currentThread().getName() + " after waits");
} catch (InterruptedException e) {
e.printStackTrace();
printThreadInfo("in wait catch ", Thread.currentThread());
}
}
try {
System.out.println(Thread.currentThread().getName() + " before sleep");
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + " after sleep");
} catch (InterruptedException e) {
e.printStackTrace();
printThreadInfo("in sleep catch ", Thread.currentThread());
}
System.out.println("end!");
}
}
}
例子上面為什么
wait()方法中為什么要加一個(gè)鎖,這里有一篇文章可以看一下java 為什么wait(),notify(),notifyAll()必須在同步方法/代碼塊中調(diào)用?
結(jié)果如下: 正如我們預(yù)想的一樣,中斷會(huì)使這些方法從阻塞狀態(tài)中提前返回,并且在拋出異常是清除了中斷狀態(tài).
mythread-1 before waits
java.lang.InterruptedException
at java.lang.Object.wait(Native Method)
at java.lang.Object.wait(Object.java:502)
at com.understand.interrupt.Test2$Runner2.run(Test2.java:35)
at java.lang.Thread.run(Thread.java:745)
in wait catch mythread-1, interrupte :false
mythread-1 before sleep
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at com.understand.interrupt.Test2$Runner2.run(Test2.java:44)
at java.lang.Thread.run(Thread.java:745)
in sleep catch mythread-1, interrupte :false
end!
但是上面的例子有個(gè)問(wèn)題就是如果不去查看
Log信息,主線(xiàn)程也就是調(diào)用者根本就不知道mythread-1線(xiàn)程發(fā)生過(guò)中斷.
所以接下來(lái)我們看看如果處理
InterruptedException異常.
處理InterruptedException異常
拋出一個(gè)
InterruptedException異常,說(shuō)明調(diào)用的方法是一個(gè)阻塞方法,因此自身來(lái)說(shuō)也是一個(gè)阻塞方法.
1.最簡(jiǎn)單的一種方式是直接繼續(xù)拋出異常給上層方法.如下所示:
public static void mySleep(long timeout) throws InterruptedException {
System.out.println("i want to sleep.");
TimeUnit.MILLISECONDS.sleep(timeout);
System.out.println("wake up");
}
2.第二種方式是捕捉該異常,做一些清理工作,比如關(guān)閉流之類(lèi)的,然后再繼續(xù)拋出異常給上層方法.
public static void mySleep1(long timeout) throws InterruptedException {
System.out.println("i want to sleep.");
try {
TimeUnit.MILLISECONDS.sleep(timeout);
} catch (InterruptedException e) {
//clear somthing
throw e;
}
System.out.println("wake up");
}
3. 第三種方式是你調(diào)用的方法中不讓拋出異常,比如例子2中的線(xiàn)程中的
run()方法,因此此時(shí)可以捕捉異常后保留中斷狀態(tài)讓調(diào)用者知道它狀態(tài),又由于在阻塞方法拋出異常的時(shí)候會(huì)清空中斷狀態(tài),所以此時(shí)可以在catch語(yǔ)句中調(diào)用Thread.currentThread().interrupt()方法設(shè)置一下中斷狀態(tài).如下所示:
static class Runner implements Runnable {
public void run() {
try {
TimeUnit.MILLISECONDS.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
Thread.currentThread().interrupt();
}
}
}
參考
1. https://www.ibm.com/developerworks/cn/java/j-jtp05236.html
2. https://blog.csdn.net/haluoluo211/article/details/49558155
3. Java并發(fā)編程的藝術(shù)