线程总共有6中状态,分别是:
New、Runnable、Blocked、Waiting、Timed Wating、Terminated
下面来分析当Runnable的run方法有while循环时,该如何在线程外部停止执行while循环的线程。
先看一段代码
public static void main(String[] args) throws Exception{
Thread t0 = new Thread(new Runnable() {
@Override
public void run() {
int num = 0;
while (num < Integer.MAX_VALUE){
System.out.println(Thread.currentThread().getName() + "正在执行");
}
}
});
t0.start();
TimeUnit.MILLISECONDS.sleep(100);
// 执行t0.interrupt(),但是t0仍会继续运行
t0.interrupt();
}
可以看到interrupt()并没有让线程停止运行。
interrput()的作用是将线程的中断状态设置为true,并不是说执行t0.interrupt()之后,t0线程就进入Terminated(终止状态)。
在以下两种情况下,线程会进入Terminated状态:
1、run方法运行结束。
2、run方法中抛出异常。
既然interrupt是将线程中断标志设置为true,那就将while循环的判断条件改成判断t0的中断状态,中断状态为false进入while循环,为true退出while循环,当run方法运行结束,线程就变为Terminated状态了。
通过 Thread.currentThread().isInterrupted() 可以获取线程的中断状态
public static void main(String[] args) throws Exception{
Thread t0 = new Thread(new Runnable() {
@Override
public void run() {
// 当前线程的中断状态为true则退出while循环
while (!Thread.currentThread().isInterrupted()){
System.out.println(Thread.currentThread().getName() + "正在执行");
}
}
});
t0.start();
TimeUnit.MILLISECONDS.sleep(100);
// 执行t0.interrupt(),t0的中断状态变为true
t0.interrupt();
}
运行上面的代码,线程确实停止了。
但是使用isInterrupted()来获取线程的中断状态需要小心,当线程t0抛出InterruptedException的时候,JVM会将t0的中断状态设置为false。
看下面这段代码
public static void main(String[] args) throws Exception{
Thread t0 = new Thread(new Runnable() {
@Override
public void run() {
while (!Thread.currentThread().isInterrupted()){
try {
System.out.println(Thread.currentThread().getName() + "正在执行");
// 在线程中加入sleep()这种可以响应中断的代码,并在while循环中把异常捕获
TimeUnit.MILLISECONDS.sleep(1);
}catch (InterruptedException e){
// 发生InterruptedException之后,线程的中断状态会被变成false
e.printStackTrace();
}
}
}
});
t0.start();
TimeUnit.MILLISECONDS.sleep(100);
t0.interrupt();
}
上面这段代码就会一直运行下去,因为发生InterruptedException之后,线程的中断状态会变为false。正确的方式应当是这样:在run方法中捕获到到InterruptedException之后,就不会再执行while循环。
public static void main(String[] args) throws Exception{
Thread t0 = new Thread(new Runnable() {
@Override
public void run() {
try {
while (!Thread.currentThread().isInterrupted()){
System.out.println(Thread.currentThread().getName() + "正在执行");
// 在线程中加入sleep()这种可以响应中断的代码
TimeUnit.MILLISECONDS.sleep(1);
}
// 将捕获异常的代码放到while循环之外,捕获异常之后不再有循环代码
}catch (InterruptedException e){
e.printStackTrace();
}
}
});
t0.start();
TimeUnit.MILLISECONDS.sleep(100);
t0.interrupt();
}
上面的代码不会一直运行,即线程t0会终止。
当我们在run方法中catch到InterruptedException一定要想到线程的中断状态被JVM自动设置为false了。
还有一种方式是使用volatile修饰一个boolean变量来停止while,但这种方式也是有bug的,不能做到立刻停止线程。
// 停止生产标识
public static volatile boolean stopProduce = false;
public static void main(String[] args) throws Exception{
// 资源队列,用于公共存储资源
LinkedBlockingQueue lbq = new LinkedBlockingQueue(3);
Thread t0 = new Thread(new Runnable() {
@Override
public void run() {
int i = 0;
try {
while (i<Integer.MAX_VALUE && !stopProduce){
System.out.println("生产 "+i);
/**
* 当LinkedBlockingQueue队列满了,put方法会将线程设置为WAIT状态
* 若没有消费者消费LinkedBlockingQueue中的资源,线程会一直在这里等待,没法运行while判断
*/
lbq.put(i);
i++;
}
}catch (InterruptedException e){
e.printStackTrace();
}
}
});
t0.start();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
try {
while (new Random().nextInt(100)>5){
// 消费资源
System.out.println("消费 "+lbq.take());
}
}catch (InterruptedException e){
e.printStackTrace();
}
}
});
t1.start();
TimeUnit.MILLISECONDS.sleep(10);
// 设置停止生产标识为true,但是t0立刻没有停止,还处于WAIT状态
stopProduce =true;
// 消费一个LinkedBlockingQueue的资源后,t0从WAIT状态变为RUNNABLE,执行while判断后退出循环,线程结束
//lbq.take();
}