转载:Java 多线程笔记1-Thread.interrupt/interrupted/isInterrupted
原文出处:http://www.blogjava.net/landon/archive/2013/12/06/407294.html
Java 多线程笔记1-Thread.interrupt/interrupted/isInterrupted
1.很多人经常会用错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.如何结束BadRunnable这样的任务.即没有任务结束条件来保证可以正常关闭它.使用interrupt没有作用,其不会中断正在运行的线程
* 2.结论:任务最好不要这样写,否则无法正常安全的关闭线程.通常需要在while()中指定任务结束条件如设置volatile变量或者判断当前线程是否已中断等或者通过投递结束消息方式(消息队列)等
* </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();
// 执行interrupt,试图终止无限循环的任务-徒劳
// thread.interrupt();
VolatileRunnable volatileTask = new VolatileRunnable();
Thread volatileThread = new Thread(volatileTask);
volatileThread.start();
// 主线程暂停5s
Thread.sleep(5 * 1000);
// 停止任务,结束volatileThread,在主线程置stopFlag(所以用volatile)
volatileTask.stop();
LOGGER.debug("VolatileRunnable end.");
Thread thread2 = new Thread(new InterruptedRunnbale());
thread2.start();
// 主线程暂停1秒
Thread.sleep(1 * 1000);
// 调用interrupte结束任务->直接中断处于sleep的任务
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));
}
// 准备停止qt
qt.prepareDispose();
}
private static class BadRunnable implements Runnable {
@Override
public void run() {
LOGGER.debug("BadRunnable begin.");
// 无限循环
while (true) {
}
}
}
private static class VolatileRunnable implements Runnable {
// 指定volatile(更新即可视) 停止标识
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.");
// 这里判断调用当前是否已被打断做判断
while (!Thread.currentThread().isInterrupted()) {
try {
// 用sleep替代业务逻辑的耗时,可被打断
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
LOGGER.debug("InterruptedRunnbale is interrupted.");
// 参考Interrupt
// API.类似调用如wait/join/sleep等方法时会收到InterruptedException且中断状态被清除
LOGGER.debug("after catch InterruptedException,thread2 isInterrupted:"
+ Thread.currentThread().isInterrupted());
// 因为中断状态被清除了.所以这次要再次调用interrupt.设置中断状态,然后任务从循环跳出.线程结束
Thread.currentThread().interrupt();
LOGGER.debug("after again execute interrupt,thread2 isInterrupted:"
+ Thread.currentThread().isInterrupted());
}
}
}
}
private static class QueueThread extends Thread {
// 阻塞消息队列
private LinkedBlockingQueue<QueueMessage> queue = new LinkedBlockingQueue<InterruptePuzzleExample.QueueMessage>();
// 因为这里通过投递内部消息方式,即在内部单线程执行.所以不用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表示停止消息(注:因为是QueueMessage内部使用,可以直接访问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可省略,因为既然执行到了dispose,则此时一定未阻塞
// interrupt();
}
// 准备销毁,由外部线程进行调用
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.很多人经常分不清interrupted和isInterrupted两个方法的区别,看例子
package com.landon.mavs.example.concurrent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*
* 使用Thread#interrupted/Thread#isInterrupted
*
* <pre>
* 1.个人认为interrupted方法是返回之前的中断状态并清除中断状态 2.而isInterrupted只是返回线程的中断状态而已
* 3.而对于interrupt方法
* ,对于诸如可抛出InterruptedException的一些方法,线程收到InterruptedException后会清除中断状态
* ;反之则会设置状态中断{仔细参考Thread#interrupt的api doc}{@link InterruptThread3}}
* </pre>
*
* <pre>
* public boolean isInterrupted() {
* return isInterrupted(false);
* }
*
* 静态方法->针对当前调用线程
* 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方法内
it3.start();
// 在外部调用iterrupt->it3收到InterruptedException->中断状态清除
it3.interrupt();
// true,因为这个是主线程调用的.所以此时it3还未被清除中断状态
LOGGER.debug("it3.isInterrupted:" + it3.isInterrupted());
// 做了一个等待.
Thread.sleep(3 * 1000);
// false,此时it3的中断状态已经被清楚
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());
// 调用interrupt,这里直接设置了中断状态
LOGGER.debug("InterruptThread execute interrupt.");
interrupt();
// true
// 调用了#interrupt->#interrupted返回true->由下面的输出可以看到,其清除了中断状态,所以下面的#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());
// 调用interrupt
LOGGER.debug("InterruptThread2 execute interrupt.");
interrupt();
// true 这里#interrupt#->isInterrupted->返回true,即该方法不影响线程的中断状态
LOGGER.debug("InterruptThread2 after interrupt.#isInterrupted:"
+ isInterrupted());
// true 这里#interrupted依然返回true并清除了中断状态.所以下面的输出返回false
LOGGER.debug("InterruptThread2 after interrupt.#interrupted:"
+ interrupted());
// false
LOGGER.debug("InterruptThread2.#isInterrupted:" + isInterrupted());
// false 这里再次调用#interrupted->返回了false.因为此时的状态状态已经为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.总结:通过代码的方式简单的总结了线程的interrupt,interrupted,isInterrupted三个方法.另外还提供了几个正确结束线程的简单方法demo.