1. 程式人生 > >Thread類中的中斷分析

Thread類中的中斷分析

interrupt()分析

先看javadoc裡的關於Thread類的interrupt()方法說明

Interrupts this thread. 

Unless the current thread is interrupting itself, which is always permitted, the checkAccess method of this thread is invoked, which may cause a SecurityException to be thrown. 

If this thread is blocked in an invocation of the wait(), wait(long), or wait(long, int) methods of the Object class, or of the join(), join(long), join(long, int), sleep(long), or sleep(long, int), methods of this class, then its interrupt status will be cleared and it will receive an InterruptedException. 

If this thread is blocked in an I/O operation upon an InterruptibleChannel then the channel will be closed, the thread's interrupt status will be set, and the thread will receive a java.nio.channels.ClosedByInterruptException. 

If this thread is blocked in a java.nio.channels.Selector then the thread's interrupt status will be set and it will return immediately from the selection operation, possibly with a non-zero value, just as if the selector's wakeup method were invoked. 

If none of the previous conditions hold then this thread's interrupt status will be set. 

Interrupting a thread that is not alive need not have any effect.

Throws:
SecurityException - if the current thread cannot modify this thread
@revised
6.0
@spec
JSR-51

執行緒的thread.interrupt()方法是中斷執行緒,將會設定該執行緒的中斷狀態位,即設定為true,中斷的結果執行緒是死亡、還是等待新的任務或是繼續執行至下一步,就取決於這個程式本身。執行緒會不時地檢測這個中斷標示位,以判斷執行緒是否應該被中斷(中斷標示值是否為true)。它並不像stop方法那樣會中斷一個正在執行的執行緒。判斷某個執行緒是否已被髮送過中斷請求,請使用Thread.currentThread().isInterrupted()方法(因為它將執行緒中斷標示位設定為true後,不會立刻清除中斷標示位,即不會將中斷標設定為false),而不要使用thread.interrupted()(該方法呼叫後會將中斷標示位清除,即重新設定為false)方法來

判斷,
除非執行緒本身自己允許中斷自己,其他執行緒中斷此執行緒,有可能會引發異常。這裡要分為兩種情況:
一種是此執行緒正在處於非阻塞狀態,,其他執行緒呼叫此類的interrupt方法時,會將執行緒的中斷標誌位設為true,(通過isInterrupted查詢),

public class InterruptTest2 extends Thread {
	
	private boolean flag=true;

	public InterruptTest2(String name){
		super(name);
	}
	
	@Override
	public void run(){
		  {  
	            int i=0;
	            while (flag) {

	               if (currentThread().isInterrupted()) {
	            	   System.out.println("已中斷");
					
				}else{
					i++;
	                System.out.println(Thread.currentThread().getName()+" ("+this.getState()+") loop " + i);  
				}
	                
	            }
	        } 
	    }
		

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		InterruptTest2 test=new InterruptTest2("t1");
		test.start();
		 try {
				Thread.sleep(300);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		 test.interrupt();
      

         // 主執行緒休眠300ms,然後檢視t1的狀態。
         try {
			Thread.sleep(300);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
     
		

	}

}

在main方法中呼叫 test.interrupt();後,test的子執行緒(run方法)會繼續執行,不停輸出已中斷
已中斷
已中斷
已中斷
已中斷
說明interrupt()方法只會設定執行緒的中斷位,而執行緒會繼續執行。(即我們需要通過獲得這個標誌位來進行中斷後的邏輯分析)。

二、如果一個執行緒處於了阻塞狀態(如執行緒呼叫了thread.sleep、thread.join、thread.wait、1.5中的condition.await、以及可中斷的通道上的 I/O 操作方法後可進入阻塞狀態),則線上程在檢查中斷標示時如果發現中斷標示為true,則會在這些阻塞方法(sleep、join、wait、1.5中的condition.await及可中斷的通道上的 I/O 操作方法)呼叫處丟擲InterruptedException異常,並且在丟擲異常後立即將執行緒的中斷標示位清除,即重新設定為false。丟擲異常是為了執行緒從阻塞狀態醒過來,並在結束執行緒前讓程式設計師有足夠的時間來處理中斷請求。

public class InterruptTest2 extends Thread {
	
	private boolean flag=true;

	public InterruptTest2(String name){
		super(name);
	}
	
	@Override
	public void run(){
		  {  
	            int i=0;
	            while (flag) {
	            	
	            	try {
						Thread.sleep(5000);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						System.out.println("執行緒狀態-->"+isInterrupted());
						flag=false;   //設定flag=false  跳出迴圈
						e.printStackTrace();
					}
	                
	            }
	        } 
	    }
		

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		InterruptTest2 test=new InterruptTest2("t1");
		test.start();
		 try {
				Thread.sleep(300);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		 test.interrupt();
      

         // 主執行緒休眠300ms,然後檢視t1的狀態。
         try {
			Thread.sleep(300);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
     
		

	}

}

執行結果:

java.lang.InterruptedException: sleep interrupted
	at java.lang.Thread.sleep(Native Method)
	at com.andy.thread.notify.InterruptTest2.run(InterruptTest2.java:33)
執行緒狀態false
t1 (RUNNABLE) loop 1

說明:正在阻塞的執行緒被外部中斷,會導致報InterruptedException 異常,然後中斷標誌位被設為false。我們可以在異常中進行相關的邏輯處理,跳出子執行緒。(程式碼中是將flag設為false,從而跳出while迴圈,最終子執行緒執行完畢)

Thread.interrupted()和Thread.currentThread().isInterrupted()(或者直接用isInterrupted())區別

Thread.interrupted(),看其javadoc說明:

Tests whether the current thread has been interrupted. The interrupted status of the thread is cleared by this method. In other words, if this method were to be called twice in succession, the second call would return false (unless the current thread were interrupted again, after the first call had cleared its interrupted status and before the second call had examined it). 

A thread interruption ignored because a thread was not alive at the time of the interrupt will be reflected by this method returning false.


Thread.interrupted()用於判斷執行緒是否被中斷,並且其中斷標誌位會在這個方法中清除。也就是說如果這個方法被成功執行2次,則第一次返回true,第二次返回false。因為在第一次呼叫過程中,其標誌位被設定為了false。

而Thread.currentThread().isInterrupted()只會返回執行緒的中斷標誌位,多次呼叫,返回值是一致的。

一個執行緒呼叫另一個執行緒的非run方法的Thread指向

這個問題是在看FutureTask的原始碼時的一點疑惑。即在main執行緒中呼叫子執行緒的非run方法,Thread.interrupted()到底是指誰?

public class InterruptTest5 extends Thread {
	
	private boolean flag=true;

	public InterruptTest5(String name){
		super(name);
	}
	
	@Override
	public void run(){
		  {  
	            int i=0;
	            while (!isInterrupted()) {
	            	
	            	try {
						Thread.sleep(1000);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}

	               if (isInterrupted()) {
	            	   System.out.println("已中斷");
					
				}else{
					i++;
	                System.out.println(Thread.currentThread().getName()+" ("+this.getState()+") loop " + i);  
				}
	                
	            }
	        } 
	    }
	
	
	
	public void stopThread(){
		
		Thread.currentThread().interrupt();
		System.out.println("stop方法中:"+Thread.currentThread().getName());

	}
	
		public void checkThread(){
			
			System.out.println("checkthread方法中:"+Thread.currentThread().getName());
			System.out.println("checkThread方法中"+Thread.currentThread().getName()+"-->"+Thread.interrupted());
			if (Thread.interrupted()) {
				System.out.println(Thread.currentThread().getName()+"執行緒被中斷");
			}
		}



}

上面是一個執行緒類,除了定義run方法,還設定了stopThread()和checkThread()方法。
在main方法中呼叫這個類。

public static void main(String[] args) {
		// TODO Auto-generated method stub
		InterruptTest5 test5=new InterruptTest5("執行緒5");
		
		test5.start();
		
  
	//	Thread.currentThread().interrupt();
		
		test5.stopThread();
		
		//System.out.println(Thread.currentThread().getName()+"-->"+Thread.interrupted());
		
		test5.checkThread();

	}

執行結果:

stop方法中:main
checkthread方法中:main
checkThread方法中main-->true
執行緒5 (RUNNABLE) loop 1
執行緒5 (RUNNABLE) loop 2
執行緒5 (RUNNABLE) loop 3

分析:main方法呼叫了子執行緒的stopThread()方法,通過列印
System.out.println(“stop方法中:”+Thread.currentThread().getName());
其執行緒名是main,說明stopThread()所處的執行緒仍然是main執行緒。
再呼叫checkThread()進行查詢執行緒狀態,通過
System.out.println(“checkThread方法中”+Thread.currentThread().getName()+"–>"+Thread.interrupted());,
其結果是
checkThread方法中main–>true
說明,主執行緒main的中斷標誌位被設為false。而子執行緒仍然在sleep,沒有被中斷。
而checkThread()中最後的:
if (Thread.interrupted()) {
System.out.println(Thread.currentThread().getName()+“執行緒被中斷”);
}
為什麼沒有被執行呢?因為上面一句
System.out.println(“checkThread方法中”+Thread.currentThread().getName()+"–>"+Thread.interrupted());,
後,其中斷標誌位被設為了false,所以後面的if沒有被執行。這也是上一節說明的