Java中断机制
引言
Java中斷機(jī)制為我們提供了一種"試圖"停止一個(gè)線程的方法。設(shè)想我們有一個(gè)線程阻塞在一個(gè)耗時(shí)的I/O中,我們又不想一直等下去,那么我們?cè)趺礃硬拍芡V惯@個(gè)線程呢?答案就是Java的中斷機(jī)制。
從Java線程的狀態(tài)說起
Java線程的狀態(tài)包括:NEW,RUNNABLE,BLOCKED,WAITING,TIMED_WAITING和TERMINATED總共六種狀態(tài):
public enum State {/*** Thread state for a thread which has not yet started.*/NEW,/*** Thread state for a runnable thread. A thread in the runnable* state is executing in the Java virtual machine but it may* be waiting for other resources from the operating system* such as processor.*/RUNNABLE,/*** Thread state for a thread blocked waiting for a monitor lock.* A thread in the blocked state is waiting for a monitor lock* to enter a synchronized block/method or* reenter a synchronized block/method after calling* {@link Object#wait() Object.wait}.*/BLOCKED,/*** Thread state for a waiting thread.* A thread is in the waiting state due to calling one of the* following methods:* <ul>* <li>{@link Object#wait() Object.wait} with no timeout</li>* <li>{@link #join() Thread.join} with no timeout</li>* <li>{@link LockSupport#park() LockSupport.park}</li>* </ul>** <p>A thread in the waiting state is waiting for another thread to* perform a particular action.** For example, a thread that has called <tt>Object.wait()</tt>* on an object is waiting for another thread to call* <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on* that object. A thread that has called <tt>Thread.join()</tt>* is waiting for a specified thread to terminate.*/WAITING,/*** Thread state for a waiting thread with a specified waiting time.* A thread is in the timed waiting state due to calling one of* the following methods with a specified positive waiting time:* <ul>* <li>{@link #sleep Thread.sleep}</li>* <li>{@link Object#wait(long) Object.wait} with timeout</li>* <li>{@link #join(long) Thread.join} with timeout</li>* <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>* <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>* </ul>*/TIMED_WAITING,/*** Thread state for a terminated thread.* The thread has completed execution.*/TERMINATED;} 復(fù)制代碼一個(gè)線程新建后,在調(diào)用該線程的start()方法之前,該線程的狀態(tài)就是NEW;當(dāng)線程的start()方法被調(diào)用之后,該線程已經(jīng)準(zhǔn)備被CPU調(diào)度,此時(shí)線程的狀態(tài)就是RUNNABLE;如果一個(gè)對(duì)象的monitor lock被其他線程獲取了,這個(gè)時(shí)候我們?cè)偻ㄟ^synchronized關(guān)鍵字去獲取改對(duì)象的monitor lock時(shí),線程就會(huì)進(jìn)入BLOCKED狀態(tài);通過調(diào)用Thread#join()方法或者Object#wait()方法(不設(shè)置超時(shí)時(shí)間,with no timeout)或者LockSupport#park()方法可以讓一個(gè)線程從RUNNABLE狀態(tài)轉(zhuǎn)為WAITING狀態(tài);TIMED_WAITING指線程處于等待中,但是這個(gè)等待是有期限的(),通過調(diào)用Thread#sleep(),Object#wait(long timeout),Thread#join(long timeout),LockSupport#parkNanos(),LockSupport#partUnitl()都可使線程切換到TIMED_WAITING狀態(tài)。最后一種狀態(tài)TERMINATED是指線程正常退出或者異常退出后的狀態(tài)。下面這張圖展示了這六種線程狀態(tài)之間的相互轉(zhuǎn)換關(guān)系:
當(dāng)線程處于等待狀態(tài)或者有超時(shí)的等待狀態(tài)時(shí)(TIMED_WAITING,WAITING)我們可以通過調(diào)用線程的interrupt()方法來中斷線程的等待,此時(shí)線程會(huì)拋InterruptedException異常。例如Thread.sleep()方法: public static native void sleep(long millis) throws InterruptedException; 此方法就會(huì)拋出這類異常。 但是當(dāng)線程處于BLOCKED狀態(tài)或者RUNNABLE(RUNNING)狀態(tài)時(shí),調(diào)用線程的interrupt()方法也只能將線程的狀態(tài)位設(shè)置為true。停止線程的邏輯需要我們自己去實(shí)現(xiàn)。
中斷原理
查看java源碼可以看到Thread類中提供了跟中斷相關(guān)的一些方法:
public void interrupt() {if (this != Thread.currentThread())checkAccess(); 1synchronized (blockerLock) {Interruptible b = blocker;if (b != null) { 2interrupt0(); // Just to set the interrupt flag 3b.interrupt(this); 4return;}}interrupt0();} 復(fù)制代碼interrupt()方法用于中斷一個(gè)線程,首先在第1處中斷方法會(huì)判斷當(dāng)前caller線程是否具有中斷本線程的權(quán)限,如果沒有權(quán)限則拋出SecurityException異常。然后在2處方法會(huì)判斷阻塞當(dāng)前線程的對(duì)象是否為空,如果不為空,則在3處先設(shè)置線程的中斷flag為true,然后再由Interruptible對(duì)象(例如可以中斷的I/O操作)去中斷操作。從中我們也可以看到如果當(dāng)前線程不處于WAITING或者TIMED_WAITING狀態(tài),則interrupt()方法也只是僅僅設(shè)置了該線程的中斷flag為true而已。
public static boolean interrupted() {return currentThread().isInterrupted(true);} 復(fù)制代碼再看interrupted()方法,該方法是一個(gè)靜態(tài)的方法,最終會(huì)調(diào)用具體線程實(shí)例的isInterrupted()方法:
public boolean isInterrupted() {return isInterrupted(false);}private native boolean isInterrupted(boolean ClearInterrupted); 復(fù)制代碼interrupted()方法第一次調(diào)用時(shí)會(huì)返回當(dāng)前線程的中斷flag,然后就清除了這個(gè)狀態(tài)位,但是isInterrupted()方法不會(huì)清除該狀態(tài)位。因此一個(gè)線程中斷后,第一次調(diào)用interrupted()方法會(huì)返回true,第二次調(diào)用就返回false,因此第一次調(diào)用后該狀態(tài)位已經(jīng)被清除了。但是同樣一個(gè)線程中斷后,多次調(diào)用isInterrupted()方法都會(huì)返回true。這是兩者的不同之處,但是兩者最終都會(huì)調(diào)用一個(gè)名為isInterrupted的native方法。
中斷的處理
java中一般方法申明了throws InterruptedException的就是可以中斷的方法,比如:ReentrantLock#lockInterruptibly,BlockingQueue#put,Thread#sleep,Object#wait等。當(dāng)這些方法被中斷后會(huì)拋出InterruptedException異常,我們可以捕獲這個(gè)異常,并實(shí)現(xiàn)自己的處理邏輯,也可以不處理,繼續(xù)向上層拋出異常。但是記住不要捕獲異常后什么都不做并且不向上層拋異常,也就是說我們不能"吞掉"異常。 對(duì)于一些沒有拋出InterruptedException的方法的中斷邏輯只能由我們自己去實(shí)現(xiàn)了。例如在一個(gè)大的循環(huán)中,我們可以自己判斷當(dāng)前線程的中斷狀態(tài)然后選擇是否中斷當(dāng)前操作:
public class InterruptTest {public static void main(String[] args) throws InterruptedException {Thread thread = new Thread(new Runnable() {@Overridepublic void run() {// 循環(huán)中檢測(cè)當(dāng)前線程的中斷狀態(tài) for (int i = 0; i < Integer.MAX_VALUE && !Thread.currentThread().isInterrupted(); i++) {System.out.println(i);}}});thread.start();Thread.sleep(100);thread.interrupt();} } 復(fù)制代碼總結(jié)
- 上一篇: 刷leetcode第705题- 设计哈希
- 下一篇: [CodeForces 892A] G