Thread.interrupt 快速解讀

        特性
        [Interrupt爲什麼有時候有用,有時候沒用呢。]
  • Interrupt設置一個線程爲中斷狀態
  • Interrupt操作的線程處於sleep,wait,join 阻塞等狀態的時候,會拋出一個InterruptedException
  • Interrupt操作的線程在可中斷通道上因調用某個阻塞的 I/O 操作(serverSocketChannel. accept()、socketChannel.connect、socketChannel.open、 
    socketChannel.read、socketChannel.write、fileChannel.read、fileChannel.write),會拋出一個ClosedByInterruptException
  • 我們通過try/catch捕獲後則可進行return/break/throw new XXException()等方式返回來銷燬或結束線程

         下面來看例子
         第一個例子解釋了爲什麼Interrupt執行後線程依然沒有中斷
         後兩個例子針對I/O阻塞以及sleep等狀態進行了中斷操作

 例子1.
/**
 * 常規interrupt
 * 
 * @author Allen
 * @date 2017年2月21日
 *
 */
public class e1 implements Runnable {
	public static void main(String[] args) {
		e1 nm=new e1();
		Thread thread = new Thread(nm);
		System.out.println("躁起來修電腦去");
		System.out.println("interrupt state = " + thread.isInterrupted());
		thread.start();
		nm.sleep(500);
		thread.interrupt();
		System.out.println("別修復了,世界末日了");
		System.out.println("interrupt state = " + thread.isInterrupted());
	}

	public void run() {
		System.out.println("開始修復電腦");
		for (int i = 10; i <= 100; i += 10) {
			if(Thread.interrupted())
				System.out.println("~~別煩我我得修完了");
			System.out.println("修復進度" + i + "%");
			sleep(200);
		}
		System.out.println("修復完畢");

	}
	/**
	 * 自己寫個sleep條件循環爲了禁止Interrupt對Thread.sleep(x)時的異常拋出
	 * @param step
	 * @author Allen
	 * @date 2017年2月21日
	 */
	private void sleep(int step) {
		long time = System.currentTimeMillis();
		while ((System.currentTimeMillis() - time < step)) {
		}
	}
}

執行結果
躁起來修電腦去
interrupt state = false
開始修復電腦
修復進度10%
修復進度20%
修復進度30%
別修復了,世界末日了
interrupt state = true
~~別煩我我得修完了
修復進度40%
修復進度50%
修復進度60%
修復進度70%
修復進度80%
修復進度90%
修復進度100%
修復完畢

上面可見在線程啓動後我們在nw.sleep(500)後,針對啓動的線程進行了thread.interrupt(),然而thread.isInterrupted()也返回了true,但是線程依然正在運行。因爲我們這裏的sleep是自己寫的並不是用的Thread.sleep或io通道所以無法通過exception進行線程終端捕獲,線程也不會拋出exception

例子2.
/**
 * interrupt try/catch銷燬版
 * 
 * @author Allen
 * @date 2017年2月21日
 *
 */
public class e2 implements Runnable {
	public static void main(String[] args) throws Exception {
		e2 nm = new e2();
		Thread thread = new Thread(nm);
		System.out.println("躁起來修電腦去"); 
		System.out.println("interrupt state = " + thread.isInterrupted());
		thread.start();
		Thread.sleep(500);
		thread.interrupt();
		System.out.println("別修復了,世界末日了");
		/**
		 * 因爲Thread.sleep 觸發了中斷狀態 所以interrupted狀態被清除
		 * 所以這裏打印的也是false
		 */
		System.out.println("interrupt state = " + thread.isInterrupted());
	}

	public void run() {
		System.out.println("開始修復電腦");
		for (int i = 10; i <= 100; i += 10) {
			if (Thread.interrupted())
				System.out.println("~~別煩我我得修完了");
			System.out.println("修復進度" + i + "%");
				try {
					Thread.sleep(200);
				} catch (InterruptedException e) {
					e.printStackTrace();
					System.out.println("拋異常了被try/catch死神捕獲了,只能return了");
					return;
				}
		}
		System.out.println("修復完畢");

	}

}

輸出

躁起來修電腦去
interrupt state = false
開始修復電腦
修復進度10%
修復進度20%
修復進度30%
別修復了,世界末日了
interrupt state = false  //這裏爲什麼返回false了呢?
java.lang.InterruptedException: sleep interrupted
	at java.lang.Thread.sleep(Native Method)
	at TwoPhaseTerminationPattern.interrupt.e2.run(e2.java:34)
	at java.lang.Thread.run(Thread.java:724)
拋異常了被try/catch死神捕獲了,只能return了

可見例子2中因爲是用了Thread.sleep,我們在調用interrupt的時候成功的在目標線程中拋出了InterruptedException,從而我們可以進行一些列操作
對於interrupte state爲什麼在這個成功捕獲的異常下居然返回false了。

我們看一下API

public boolean isInterrupted()
Tests whether this thread has been interrupted. The interrupted status of the thread is unaffected by this method.
A thread interruption ignored because a thread was not alive at the time of the interrupt will be reflected by this method returning false.

Returns:
true if this thread has been interrupted; false otherwise.
See Also:
interrupted()

原來是因爲當拋出exception後線程中斷重置狀態了所以返回false了

例子3 io阻塞下的interrupt

/**
 * interrupt i/o阻塞
 * 
 * @author Allen
 * @date 2017年2月21日
 *
 */
public class e3 implements Runnable {
	public static void main(String[] args) throws Exception {
		e3 nm = new e3();
		Thread thread = new Thread(nm);
		System.out.println("燥起來修電腦去");
		System.out.println("interrupt state = " + thread.isInterrupted());
		thread.start();
		Thread.sleep(800);
		thread.interrupt();
		System.out.println("別修復了,世界末日了");
		System.out.println("interrupt state = " + thread.isInterrupted());
	}

	public void run() {
		System.out.println("開始修復電腦");
		for (int i = 10; i <= 100; i += 10) {
			if (Thread.interrupted())
				System.out.println("~~別煩我我得修完了");
			System.out.println("修復進度" + i + "%");
			if (i == 70) {
				try {
					System.out.println("建立一個nio ServerSocketChannel我就繼續修復");
					ServerSocketChannel ss = ServerSocketChannel.open();
					ss.socket().bind(new InetSocketAddress(4488));
					System.out.println("建立好了");
					while (true) {
						// 阻塞一下
						ss.accept();
					}
				} catch (IOException e) {
					e.printStackTrace();
					System.out.println("拋異常了被try/catch死神補貨了,只能return了");
					return;
				}
			}

		}
		System.out.println("修復完畢");

	}
}

輸出

燥起來修電腦去
interrupt state = false
開始修復電腦
修復進度10%
修復進度20%
修復進度30%
修復進度40%
修復進度50%
修復進度60%
修復進度70%
建立一個nio ServerSocketChannel我就繼續修復
建立好了
別修復了,世界末日了
interrupt state = true  //這裏怎麼又出現true了,捕獲了exception不是應該重置狀態麼?
java.nio.channels.ClosedByInterruptException
	at java.nio.channels.spi.AbstractInterruptibleChannel.end(AbstractInterruptibleChannel.java:202)
	at sun.nio.ch.ServerSocketChannelImpl.accept(ServerSocketChannelImpl.java:248)
	at TwoPhaseTerminationPattern.interrupt.e3.run(e3.java:41)
	at java.lang.Thread.run(Thread.java:724)
拋異常了被try/catch死神補貨了,只能return了

爲什麼io例子中interrupt的狀態又成了true?
又翻了翻API

public void 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 interruptible channel then the channel will be closed, the thread's interrupt status will be set, and the thread will receive a ClosedByInterruptException.

If this thread is blocked in a 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


沒關係還有中文的jdk7

public void interrupt()
中斷線程。
如果當前線程沒有中斷它自己(這在任何情況下都是允許的),則該線程的 checkAccess 方法就會被調用,這可能拋出 SecurityException。

如果線程在調用 Object 類的 wait()、wait(long) 或 wait(long, int) 方法,或者該類的 join()、join(long)、join(long, int)、sleep(long) 或 sleep(long, int) 方法過程中受阻,則其中斷狀態將被清除,它還將收到一個 InterruptedException。

如果該線程在可中斷的通道上的 I/O 操作中受阻,則該通道將被關閉,該線程的中斷狀態將被設置並且該線程將收到一個 ClosedByInterruptException。

如果該線程在一個 Selector 中受阻,則該線程的中斷狀態將被設置,它將立即從選擇操作返回,並可能帶有一個非零值,就好像調用了選擇器的 wakeup 方法一樣。

如果以前的條件都沒有保存,則該線程的中斷狀態將被設置。

中斷一個不處於活動狀態的線程不需要任何作用。

拋出:
SecurityException - 如果當前線程無法修改該線程

可以看到當wait,join.sleep因interrupt而拋出InterruptedExcetion前,還會清除/重置其中斷狀態爲false,所以例子2中顯示爲false
然而IO操作中,僅僅是拋出一個ClosedByInterruptException 所以interrupted顯示true



就到這裏了,相信大家對interrupt的用法已經有一定了解了。微笑



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章