1.很多人經(jīng)常會用錯interrupt方法,直接看例子
package com.landon.mavs.example.concurrent;

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


/** *//**
*
* interrupt容易使用出錯的例子
*
* <pre>
* 1.如何結(jié)束BadRunnable這樣的任務(wù).即沒有任務(wù)結(jié)束條件來保證可以正常關(guān)閉它.使用interrupt沒有作用,其不會中斷正在運(yùn)行的線程
* 2.結(jié)論:任務(wù)最好不要這樣寫,否則無法正常安全的關(guān)閉線程.通常需要在while()中指定任務(wù)結(jié)束條件如設(shè)置volatile變量或者判斷當(dāng)前線程是否已中斷等或者通過投遞結(jié)束消息方式(消息隊(duì)列)等
* </pre>
*
* <p>
* <a href=
* "http://docs.oracle.com/javase/6/docs/technotes/guides/concurrency/threadPrimitiveDeprecation.html"
* >為何Thread#stop/resume/suspend被棄用</a>
* <p>
*
* @author landon
*
*/

public class InterruptePuzzleExample
{
private static final Logger LOGGER = LoggerFactory
.getLogger(InterruptePuzzleExample.class);


public static void main(String[] args) throws Throwable
{

// Thread thread = new Thread(new BadRunnable());

// thread.start();
// 執(zhí)行interrupt,試圖終止無限循環(huán)的任務(wù)-徒勞
// thread.interrupt();

VolatileRunnable volatileTask = new VolatileRunnable();
Thread volatileThread = new Thread(volatileTask);
volatileThread.start();

// 主線程暫停5s
Thread.sleep(5 * 1000);
// 停止任務(wù),結(jié)束volatileThread,在主線程置stopFlag(所以用volatile)
volatileTask.stop();

LOGGER.debug("VolatileRunnable end.");

Thread thread2 = new Thread(new InterruptedRunnbale());
thread2.start();

// 主線程暫停1秒
Thread.sleep(1 * 1000);
// 調(diào)用interrupte結(jié)束任務(wù)->直接中斷處于sleep的任務(wù)
thread2.interrupt();

LOGGER.debug("main_thread2 isInterrupted:" + thread2.isInterrupted());

QueueThread qt = new QueueThread();
qt.start();


for (int i = 1; i < 5; i++)
{
qt.offerMessage(new QueueMessage(i));
}

// 準(zhǔn)備停止qt
qt.prepareDispose();

}


private static class BadRunnable implements Runnable
{

@Override

public void run()
{
LOGGER.debug("BadRunnable begin.");

// 無限循環(huán)

while (true)
{
}
}

}


private static class VolatileRunnable implements Runnable
{
// 指定volatile(更新即可視) 停止標(biāo)識
private volatile boolean stopFlag;


public void stop()
{
stopFlag = true;
}

@Override

public void run()
{
LOGGER.debug("VolatileRunnable begin.");


while (!stopFlag)
{
}
}
}


private static class InterruptedRunnbale implements Runnable
{

@Override

public void run()
{
LOGGER.debug("InterruptedRunnbale begin.");

// 這里判斷調(diào)用當(dāng)前是否已被打斷做判斷

while (!Thread.currentThread().isInterrupted())
{

try
{
// 用sleep替代業(yè)務(wù)邏輯的耗時,可被打斷
TimeUnit.SECONDS.sleep(3);

} catch (InterruptedException e)
{
LOGGER.debug("InterruptedRunnbale is interrupted.");

// 參考Interrupt
// API.類似調(diào)用如wait/join/sleep等方法時會收到InterruptedException且中斷狀態(tài)被清除
LOGGER.debug("after catch InterruptedException,thread2 isInterrupted:"
+ Thread.currentThread().isInterrupted());
// 因?yàn)橹袛酄顟B(tài)被清除了.所以這次要再次調(diào)用interrupt.設(shè)置中斷狀態(tài),然后任務(wù)從循環(huán)跳出.線程結(jié)束
Thread.currentThread().interrupt();
LOGGER.debug("after again execute interrupt,thread2 isInterrupted:"
+ Thread.currentThread().isInterrupted());
}
}
}
}


private static class QueueThread extends Thread
{
// 阻塞消息隊(duì)列
private LinkedBlockingQueue<QueueMessage> queue = new LinkedBlockingQueue<InterruptePuzzleExample.QueueMessage>();
// 因?yàn)檫@里通過投遞內(nèi)部消息方式,即在內(nèi)部單線程執(zhí)行.所以不用volatile
private boolean stopFlag;

@Override

public void run()
{
LOGGER.debug("QueueThread begin.");


while (!stopFlag)
{

try
{
QueueMessage msg = queue.take();


if (msg != null)
{
LOGGER.debug("QueueThread process msg:" + msg);

// -1表示停止消息(注:因?yàn)槭荙ueueMessage內(nèi)部使用,可以直接訪問private屬性)

if (msg.msgType == -1)
{
dispose();
}
}


} catch (InterruptedException e)
{
LOGGER.debug("QueueMessage is interrupted.take is notify.");
}
}
}


public void offerMessage(QueueMessage msg)
{
queue.offer(msg);
}


public void dispose()
{
stopFlag = true;
// 這里interrupt可省略,因?yàn)榧热粓?zhí)行到了dispose,則此時一定未阻塞
// interrupt();
}

// 準(zhǔn)備銷毀,由外部線程進(jìn)行調(diào)用

public void prepareDispose()
{
LOGGER.debug("QueueThread prepare dispose.");
offerMessage(new QueueMessage(-1));
}
}


private static class QueueMessage
{
// 消息類型
private int msgType;


public QueueMessage(int type)
{
msgType = type;
}

@Override

public String toString()
{
return "QueueMessage [msgType=" + msgType + "]";
}

}
}

2.很多人經(jīng)常分不清interrupted和isInterrupted兩個方法的區(qū)別,看例子
package com.landon.mavs.example.concurrent;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


/** *//**
*
* 使用Thread#interrupted/Thread#isInterrupted
*
* <pre>
* 1.個人認(rèn)為interrupted方法是返回之前的中斷狀態(tài)并清除中斷狀態(tài) 2.而isInterrupted只是返回線程的中斷狀態(tài)而已
* 3.而對于interrupt方法
* ,對于諸如可拋出InterruptedException的一些方法,線程收到InterruptedException后會清除中斷狀態(tài)
* ;反之則會設(shè)置狀態(tài)中斷{仔細(xì)參考Thread#interrupt的api doc}{@link InterruptThread3}}
*【landon認(rèn)為因阻塞的線程被interrupt后,雖然是收到了異常,但是卻中斷了阻塞,其實(shí)是可以繼續(xù)運(yùn)行的!所以會清除中斷狀態(tài)】
* </pre>
* <pre>
* if (Thread.interrupted()) // Clears interrupted status!
* throw new InterruptedException();
* </pre>
*
* <pre>
* public boolean isInterrupted() {
* return isInterrupted(false);
* }
*
* 靜態(tài)方法->針對當(dāng)前調(diào)用線程
* public static boolean interrupted() {
* return currentThread().isInterrupted(true);
* }
*
* private native boolean isInterrupted(boolean ClearInterrupted);
* </pre>
*
* @author landon
*
*/

public class ThreadInterruptedExample
{
private static final Logger LOGGER = LoggerFactory
.getLogger(ThreadInterruptedExample.class);


public static void main(String[] args) throws Exception
{
InterruptThread it = new InterruptThread();
it.start();

InterruptThread2 it2 = new InterruptThread2();
it2.start();

InterruptThread3 it3 = new InterruptThread3();
// 此時it3阻塞在wait方法內(nèi)
it3.start();
// 在外部調(diào)用iterrupt->it3收到InterruptedException->中斷狀態(tài)清除
it3.interrupt();

// true,因?yàn)檫@個是主線程調(diào)用的.所以此時it3還未被清除中斷狀態(tài)
LOGGER.debug("it3.isInterrupted:" + it3.isInterrupted());
// 做了一個等待.
Thread.sleep(3 * 1000);
// false,此時it3的中斷狀態(tài)已經(jīng)被清楚
LOGGER.debug("it3.isInterrupted:" + it3.isInterrupted());
}


private static class InterruptThread extends Thread
{
@Override

public void run()
{
// false
LOGGER.debug("InterruptThread before interrupt.#interrupted:"
+ interrupted());
// false
LOGGER.debug("InterruptThread before interrupt.#isInterrupted:"
+ isInterrupted());

// 調(diào)用interrupt,這里直接設(shè)置了中斷狀態(tài)
LOGGER.debug("InterruptThread execute interrupt.");
interrupt();

// true
// 調(diào)用了#interrupt->#interrupted返回true->由下面的輸出可以看到,其清除了中斷狀態(tài),所以下面的#isInterrupted返回了false
LOGGER.debug("InterruptThread after interrupt.#interrupted:"
+ interrupted());
// false
LOGGER.debug("InterruptThread after interrupt.#isInterrupted:"
+ isInterrupted());
}
}


private static class InterruptThread2 extends Thread
{
@Override

public void run()
{
// false
LOGGER.debug("InterruptThread2 before interrupt.#interrupted:"
+ interrupted());
// false
LOGGER.debug("InterruptThread2 before interrupt.#isInterrupted:"
+ isInterrupted());

// 調(diào)用interrupt
LOGGER.debug("InterruptThread2 execute interrupt.");
interrupt();

// true 這里#interrupt#->isInterrupted->返回true,即該方法不影響線程的中斷狀態(tài)
LOGGER.debug("InterruptThread2 after interrupt.#isInterrupted:"
+ isInterrupted());

// true 這里#interrupted依然返回true并清除了中斷狀態(tài).所以下面的輸出返回false
LOGGER.debug("InterruptThread2 after interrupt.#interrupted:"
+ interrupted());

// false
LOGGER.debug("InterruptThread2.#isInterrupted:" + isInterrupted());

// false 這里再次調(diào)用#interrupted->返回了false.因?yàn)榇藭r的狀態(tài)狀態(tài)已經(jīng)為false了
LOGGER.debug("InterruptThread2.#interrupted:" + interrupted());

}
}


private static class InterruptThread3 extends Thread
{
private final Object lock = new Object();

@Override

public void run()
{

synchronized (lock)
{

try
{
lock.wait();

} catch (InterruptedException e)
{
LOGGER.debug("InterruptThread3#wait,is interrupted..");
// false
LOGGER.debug("InterruptThread3#wati,receive InterruptedException.#isInterrupted:"
+ isInterrupted());
}
}
}
}
}

3.總結(jié):通過代碼的方式簡單的總結(jié)了線程的interrupt,interrupted,isInterrupted三個方法.另外還提供了幾個正確結(jié)束線程的簡單方法demo.
posted on 2013-12-06 17:11
landon 閱讀(2041)
評論(0) 編輯 收藏 所屬分類:
Program