快速解读请看这篇:https://blog.csdn.net/crazyzxljing0621/article/details/56666418
对Thread.interrupt一直处于模模糊糊的状态。
每个线程都有一个中断标记,中断标记默认是false.
Thread.interrupt的主要作用是 给指定线程设置中断标记为true.这就会产生一些影响
如果线程正在运行状态,Thread.interrupt的操作,只会对线程的状态做出改变,没有其他实质性的操作,如果你真的向终端该线程的操作,就需要不断检测线程的状态,Thread.interrupted(),如果位true,则手动进行中断操作
如果线程因为wait join sleep等方法阻塞,中断标记为true将导致 抛出异常InterruptedException,并把中断标记重置为false
如果线程因为IO阻塞,中断标记为true将导致 抛出异常ClosedByInterruptException,但是,但是,但是 不会重置中断标记.也就是说中断标记依然为false
如果中断线程因为nio.channels.Selector阻塞,中断标记为true将导致 线程立即返回一个特殊值,不会,不会,不会抛异常,不会,不会,不会重置中断标记,也就是说中断标记依然为false
但是我想说的是下面的这个场景:
如果我们把线程池的某个线程做interrupt的操作后,这个线程池中的线程结束任务后还能不能用呢?
答案是 可以继续使用,线程池将会通过Thread.interrupted
操作,重置线程中断状态.注意不是Thread.interrupt
这个方法
Thread.interrupted
能返回这个线程的中断状态,并重置中断状态.也就是说线程A的中断状态如果是true.那么Thread.interrupted
返回true.再次调用thread.isinterrupted
将返回false
下面将结合我的测试和源码来证明上面的观点
- 首先证明
Thread.interrupted();
的特性
public static void main(String[] args) {
System.out.println(Thread.currentThread().isInterrupted());//expected false
Thread.currentThread().interrupt();
System.out.println(Thread.currentThread().isInterrupted());//expected true
boolean interrupted = Thread.interrupted();
System.out.println(interrupted);//expected true
System.out.println(Thread.currentThread().isInterrupted());//expected false
}
image.png
- 证明线程池的线程回收时,会重置中断状态
public static void main(String[] args) throws InterruptedException {
//通过threadlocal验证这2次使用的线程是否为同一个
ThreadLocal<Integer> localMap = new ThreadLocal<>();
//创建只有一个线程的线程池
ExecutorService executorService = Executors.newSingleThreadExecutor();
final Thread[] vice = new Thread[1];
executorService.submit( () ->{
vice[0] = Thread.currentThread();
System.out.println(Thread.currentThread().getName());
localMap.set(22);
try {
while (true){
//检测线程中断状态是否为false
AssertUtil.assertFalse("被终止",Thread.currentThread().isInterrupted());
}
} catch (Exception e) {
System.out.println(Thread.currentThread().isInterrupted());
e.printStackTrace();
}
} );
Thread.sleep(300);
vice[0].interrupt();
executorService.submit(() -> {
//如果还能取出22说明,线程还是原来的线程
System.out.println(localMap.get());
//打印线程名
System.out.println(Thread.currentThread().getName());
//查看线程中断状态
System.out.println(Thread.currentThread().isInterrupted());
});
//销毁线程池
executorService.shutdown();
}
引入ThreadLocal是因为,当我发现线程名字一样时.还以为是线程池把之前的线程销毁了,然后再重新创建了一个线程,毕竟无法充线程名来判断是否为同一个线程,于是我就引入了ThreadLocal来测试了下.
翻源码:
线程池在提交任务时才会去创建一个work,所以我们就从线程池内部的work类看起,看线程完成任务后会经历什么
private final class Worker
extends AbstractQueuedSynchronizer
implements Runnable
{
/**
* This class will never be serialized, but we provide a
* serialVersionUID to suppress a javac warning.
*/
private static final long serialVersionUID = 6138294804551838833L;
/** Thread 引用 */
final Thread thread;
/** 初始任务,一般就是我们提交的任务. */
Runnable firstTask;
/**
* 构造方法
*/
Worker(Runnable firstTask) {
this.firstTask = firstTask;
//创建线程并 执行work的run方法
this.thread = getThreadFactory().newThread(this);
}
/**线程方法,继承自Runnable方法,原来work本身也是一个Runnable */
public void run() {
runWorker(this);
}
}
runWorker()方法是ThreadPoolExecutor的方法
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock(); // allow interrupts
boolean completedAbruptly = true;
try {
while (task != null || (task = getTask()) != null) {
w.lock();
// 当线程池停止时,确保线程中断标志位true
// 当线程正在运行,要确保线程的中断状态处于false,也就是说,会重置线程中断状态
if ((runStateAtLeast(ctl.get(), STOP) ||
//看到了熟悉的Thread.interrupted()来重置线程状态了
(Thread.interrupted() &&
runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted())
wt.interrupt();
try {
....略
}
}
completedAbruptly = false;
} finally {
processWorkerExit(w, completedAbruptly);
}
}
网友评论