1.何谓线程中断
线程中断,可以理解为一个现成的标识属性,它表示一个运行中的线程是否被其他线程进行了中断操作,中断可以用来进行强制终结线程(使线程进入终止状态),即在执行run方法过程中直接退出(或者说跳过某段代码)。
线程中断的方法:
1)stop()方法:不在使用此方法,其中断线程是立即中断的,即使是在同步代码块中对数据进行操作时也会立即终止,详细不在赘述,只需知道该方法不在使用即可。
2)interrupt()方法:其他线程调用此线程的interrupt()方法对其进行中断操作时,interrupt()方法将线程的中断状态设置为true,如果该线程正处于阻塞或即将处于阻塞状态,则会发生InterruptedException并被抛出,当抛出该异常或使用interrupted()方法判断是否中断时将会重置中断状态为false。
1.1 详解interrupt()方法实现线程中断操作(阻塞线程中断)
1) 当其他线程调用某线程的interrupt()方法时,仅仅时将该线程的中断状态设置为true,并不是一定会立即将该线程中断使其进入终止状态,相当于其他线程对该线程说“兄弟,你要结束了”。如果这个线程处于阻塞或是即将进入阻塞状态时则会发生中断,也可以设置一些中断条件,通过与isInterrupted()方法进行&&操作实现中断。代码如下:
public class Demo8 extends Thread{
public static void main(String[] args) {
Demo8 itt = new Demo8();
itt.start();
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
itt.interrupt();
}
public void run() {
// 这里调用的是非清除中断标志位的isInterrupted方法
while(!Thread.currentThread().isInterrupted()) {
System.out.println(Thread.currentThread().getName() + " 正在运行");
try {
System.out.println(Thread.currentThread().getName() + "线程开始阻塞");
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + " 线程阻塞结束");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
//由于调用sleep()方法清除状态标志位 所以这里需要再次重置中断标志位 否则线程会继续运行下去
Thread.currentThread().interrupt();
e.printStackTrace();
}
}
System.out.println("跳出循环");
if (Thread.currentThread().isInterrupted()) {
System.out.println(Thread.currentThread().getName() + "线程被中断");
}
}
}
2)有两种情况无法发生中断:不能中断正在获取synchronized锁或者试图执行IO操作的线程,代码如下
public class Demo9 {
public static void main(String[] args) throws Exception {
InputStream in=System.in;//不会被中断
Thread t1=new Thread(new IOBlocked(in));
Thread t2=new Thread(new SynchronizedBlocked());
t1.start();
t2.start();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
t1.interrupt();
t2.interrupt();
}
}
class IOBlocked implements Runnable{
private InputStream in;
public IOBlocked(InputStream is){
in=is;
}
public void run(){
try{
while(true){
System.out.println("等待进行io操作");
in.read();//试图进行IO操作
}
}catch (IOException e) {
if(Thread.currentThread().isInterrupted()){
System.out.println("IO thread is Interrupted");
}else{
throw new RuntimeException(e);
}
}
System.out.println("io操作运行中");
}
}
class SynchronizedBlocked implements Runnable{
public synchronized void f(){
//永不释放锁
while(true){
Thread.yield();
}
}
public SynchronizedBlocked(){
new Thread(){
public void run(){
SynchronizedBlocked.this.f();//在此线程中获取锁,并且不进行释放
}
}.start();
}
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("尝试调用f()");
f();//在此处尝试获取锁
System.out.println("f()运行中");
}
}
3)对于无法中断的IO操作,我们可以通过关闭任务中发生阻塞的底层资源,如上代码中的System.in可以通过关闭流的方式来进行中断。
4)对于无法中断互斥所引起的阻塞,我们可以使用ReentrantLock类的tryLock方法或.lockInterruptibly()方法即可解决。
1.2 检查线程中断(实现非阻塞线程中断)
1)当调用某个线程的interrupt方法时,中断发生的唯一时刻是在任务即将进入到阻塞操作中时或者已经在阻塞操作内部,若在某个死循环中添加了可能会产生阻塞操作的代码,但恰巧没有发生任何阻塞的情况下,我们需要另一种方式来退出。
public class InterruptThreadTest extends Thread{
public void run() {
// 这里调用的是非清除中断标志位的isInterrupted方法
while(!Thread.currentThread().isInterrupted()) {
long beginTime = System.currentTimeMillis();
System.out.println(Thread.currentThread().getName() + "is running");
// 当前线程每隔一秒钟检测线程中断标志位是否被置位
while (System.currentTimeMillis() - beginTime < 1000) {}
}
if (Thread.currentThread().isInterrupted()) {
System.out.println(Thread.currentThread().getName() + "is interrupted");
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
InterruptThreadTest itt = new InterruptThreadTest();
itt.start();
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// 设置线程的中断标志位
itt.interrupt();
}
}
2)检查线程中断的方法:
isInterrupted()方法判断当前线程是否为中断状态
interrupted()方法判断当前线程是否为中断状态,并且会重置线程中断状态为false
3)如何正确的安全的终止线程:中断状态是线程的一个标识位,而中断操作是一种简便的线程间交互 方式,而这种交互方式最适合用来取消或停止任务。除了中断以外,还可以利用一个boolean变 量来控制是否需要停止任务并终止该线程。这种通过标识位或者中断操作的方式能够使线程在终止时有机会去清理资源,而不是武断地将线程停止,因此这种终止线程的做法显得更加安全和优雅。
public class Shutdown {
public static void main(String[] args) throws Exception {
Runner one = new Runner();
Thread countThread = new Thread(one, "CountThread");
countThread.start();
// 睡眠1秒,main线程对CountThread进行中断,使CountThread能够感知中断而结束
Thread.sleep(1);
countThread.interrupt();
Runner two = new Runner();
countThread = new Thread(two, "CountThread");
countThread.start();
// 睡眠1秒,main线程对Runner two进行取消,使CountThread能够感知on为false而结束
Thread.sleep(1);
two.cancel();
}
private static class Runner implements Runnable {
private long i;
private volatile boolean on = true;
@Override
public void run() {
while (on && !Thread.currentThread().isInterrupted()) {
i++;
}
System.out.println("Count i = " + i);
}
public void cancel() {
on = false;
}
}
}