我的家園

          我的家園

          [本文是我對Java Concurrency In Practice 7.1的歸納和總結. ?轉載請注明作者和出處, ?如有謬誤, 歡迎在評論中指正. ]

          ?

          在java的中斷機制中, InterruptedException異常占據重要的位置. 處理InterruptedException異常的方式有:

          ?

          1. 不catch直接向上層拋出, 或者catch住做一些清理工作之后重拋該異常. 這樣的處理使得你的方法也成為一個可中斷的阻塞方法:

          // 直接向上層拋出InterruptedException, dosomething方法也是一個可中斷的阻塞方法
          private void dosomething() throws InterruptedException {
          	Thread.sleep(1000);
          }
          ??

          2. 有時不能向上拋出InterruptedException異常(例如父類的相應方法沒有聲明該異常), 此時catch之后, 必須設置當前線程的中斷標記為true, 以表明當前線程發生了中斷, 以便調用棧上層進行處理:

          public class InterruptedExceptionHandler implements Runnable {
          	private Object lock = new Object();
          
          	@Override
          	public void run() {
          		while (!Thread.currentThread().isInterrupted()) {
          			dosomething();
          		}
          	}
          
          	private void dosomething() {
          		try {
          			// Object.wait是一個可中斷的阻塞方法, 如果在其阻塞期間檢查到當前線程的中斷標記為true, 會重置中斷標記后從阻塞狀態返回, 并拋出InterruptedException異常
          			synchronized (lock) {
          				lock.wait();
          			}
          		} catch (InterruptedException e) {
          			System.out.println("InterruptedException happened");
          			// catch住InterruptedException后設置當前線程的中斷標記為true, 以供調用棧上層進行相應的處理
          			// 在此例中, dosomething方法的調用棧上層是run方法.
          			Thread.currentThread().interrupt();
          		}
          	}
          	
          	public static void main(String[] args) throws InterruptedException {
          		Thread t = new Thread(new InterruptedExceptionHandler());
          		t.start();
          		// 啟動線程1s后設置其中斷標記為true
          		Thread.sleep(1000);
          		t.interrupt();
          	}
          }?

          主線程啟動InterruptedExceptionHandler線程1s后, 設置InterruptedExceptionHandler線程的中斷標記為true. 此時InterruptedExceptionHandler線程應該阻塞在wait方法上, 由于wait方法是可中斷的阻塞方法, 所以其檢查到中斷標記為true時, 將重置當前線程的中斷標記后拋出InterruptedException, dosomething方法catch住InterruptedException異常后, 再次將當前線程的中斷標記設置為true, run方法檢查到中斷標記為true, 循環不再繼續. 假如dosomething方法catch住InterruptedException異常后沒有設置中斷標記, 其調用棧上層的run方法就無法得知線程曾經發生過中斷, 循環也就無法終止.

          ?

          3. 還有一種情形比較特殊: 我們希望發生了InterruptedException異常后仍然繼續循環執行某阻塞方法, 此時應該將中斷狀態保存下來, 當循環完成后再根據保存下來的中斷狀態執行相應的操作:

          public class InterruptedExceptionContinueHandler implements Runnable {
          	private BlockingQueue<Integer> queue;
          
          	public InterruptedExceptionContinueHandler(BlockingQueue<Integer> queue) {
          		this.queue = queue;
          	}
          
          	@Override
          	public void run() {
          		while (!Thread.currentThread().isInterrupted()) {
          			dosomething();
          		}
          		System.out.println(queue.size());
          	}
          
          	private void dosomething() {
          		// cancelled變量用于表明線程是否發生過中斷
          		boolean cancelled = false;
          		for (int i = 0; i < 10000; i++) {
          			try {
          				queue.put(i);
          			} catch (InterruptedException e) {
          				// 就算發生了InterruptedException, 循環也希望繼續運行下去, 此時將cancelled設置為true, 以表明遍歷過程中發生了中斷
          				System.out.println("InterruptedException happened when i = " + i);
          				cancelled = true;
          			}
          		}
          		// 如果當前線程曾經發生過中斷, 就將其中斷標記設置為true, 以通知dosomething方法的上層調用棧
          		if (cancelled) {
          			Thread.currentThread().interrupt();
          		}
          	}
          	
          	public static void main(String[] args) throws InterruptedException {
          		Thread t = new Thread(new InterruptedExceptionContinueHandler(new LinkedBlockingQueue<Integer>()));
          		t.start();
          		
          		// 啟動線程2ms后設置其中斷標記為true
          		Thread.sleep(2);
          		t.interrupt();
          	}
          }

          在我的機器中, 輸出結果如下:

          InterruptedException happened when i = 936

          size = 9999

          隊列的size是9999而不是10000, 是因為i = 936時發生了InterruptedException異常, 該次put沒有成功.

          為什么不在發生InterruptedException時就設置當前線程的中斷標記, 而非要繞一圈? 假設將dosomething方法改為:

          private void dosomething() {
          	for (int i = 0; i < 10000; i++) {
          		try {
          			queue.put(i);
          		} catch (InterruptedException e) {
          			System.out.println("InterruptedException happened when i = " + i);
          			Thread.currentThread().interrupt();
          		}
          	}
          }

          運行后發現結果類似為:

          InterruptedException happened when i = 936

          InterruptedException happened when i = 937

          ...

          InterruptedException happened when i = 9998

          InterruptedException happened when i = 9999

          size = 936

          catch住InterruptedException后立即將當前線程的中斷標記設置為true, 就會導致put方法又拋出InterruptedException異常, 如此往復直到循環結束.

          ?

          4. 最不可取的是catch了InterruptedException異常但是不做任何處理, 這樣一來調用棧上層就無法得知當前線程是否發生過中斷. 只有一種情況下可以這樣處理: 當InterruptedException發生在調用棧的最上層, 如run方法, 或者main方法中, 且后續代碼不檢查中斷狀態時:

          public static void main(String[] args) {
          	// main方法已經是調用棧的最上層, 此時可以catchInterruptedException后不做任何處理
          	try {
          		Thread.sleep(2);
          	} catch (InterruptedException e) {
          		e.printStackTrace();
          	}
          }





          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          主站蜘蛛池模板: 孟州市| 杭州市| 冀州市| 沙洋县| 永兴县| 临武县| 新化县| 乌兰浩特市| 开平市| 梁平县| 海门市| 荔波县| 高邑县| 鲁山县| 新宁县| 沅陵县| 石棉县| 宜君县| 贺兰县| 牟定县| 青海省| 邻水| 邯郸市| 屏山县| 铜山县| 黄陵县| 平邑县| 桐城市| 偏关县| 苍溪县| 龙井市| 蓝田县| 烟台市| 库车县| 泽州县| 临西县| 阜阳市| 射阳县| 上栗县| 汕头市| 惠州市|