您的位置:首页 > 编程语言 > Java开发

JAVA线程中断

2013-11-06 23:44 197 查看
Java中断机制是一种协作机制,也就是说通过中断并不能直接终止另一个线程,而需要被中断的线程自己处理中断

Java中的每个线程都有一个boolean的中断标志位(不是Thread的成员变量。),代表是否有中断请求。

Thread类提供3个和中断标志位有关的方法:

public void interrupt:

中断线程,如果一个线程正在被wait,join,sleep方法阻塞,调用此方法将清除中断标志位并且抛出InterruptedException异常。

public static boolean interrupted:

检测线程是否中断,调用此方法会清除中断标志位,如果一个线程处于被中断状态,调用此方法返回true,在下次被中断前再次调用的话,返回的会是false,因为他的中断标志已经被clear了

public boolean isInterrupted:

检测线程是否中断,不影响此线程的中断标志位。

concurrent类库下进行多线程编程已经很少直接对Thread对象直接进行操作,所以很少直接调用interrupt方法。如果在ExecutorService的实现类上调用shutdownNow,它其实是让线程池内的所有线程调用interrupt,比如ThreadPoolExecutor的shutdownNow方法:

public List<Runnable> shutdownNow() {
List<Runnable> tasks;
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
advanceRunState(STOP);
interruptWorkers();
tasks = drainQueue();
} finally {
mainLock.unlock();
}
tryTerminate();
return tasks;
}
interruptWorkers的实现:

private void interruptWorkers() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
for (Worker w : workers) {
try {
w.thread.interrupt();
} catch (SecurityException ignore) {
}
}
} finally {
mainLock.unlock();
}
}

如果想对单个线程发送中断请求,用Executor的submit方法启动任务,通过submit返回的Future对象,调用其cancel方法。

private static void tryInterrupt(Runnable r) throws InterruptedException {
Future<?> f = exec.submit(r);
f.cancel(true);
}
下面代码演示了试图中断不同类型的阻塞线程:

public class TestInterrupt {
private static ExecutorService exec =
Executors.newCachedThreadPool();
private static void tryInterrupt(Runnable r) throws InterruptedException {
Future<?> f = exec.submit(r);
TimeUnit.MICROSECONDS.sleep(100);
System.out.println("Interrupting " + r.getClass().getName());
f.cancel(true);
System.out.println("Interrupt sent to " + r.getClass().getName());
}

public static void main(String[] args) throws Exception {
tryInterrupt(new SleepBlocked());
tryInterrupt(new IOBlocked(System.in));
tryInterrupt(new SynchronizedBlocked());
TimeUnit.SECONDS.sleep(3);
System.out.println("aborting with system.exit(0)");
System.exit(0);
}
}

/**
* @description sleep阻塞
*/
class SleepBlocked implements Runnable {

@Override
public void run() {
try {
TimeUnit.SECONDS.sleep(100);
} catch (InterruptedException e) {
System.out.println("InterruptedException");
}
System.out.println("Existing SleepBlock.run()");
}
}

/**
* @description I/O阻塞
*/
class IOBlocked implements Runnable {

private InputStream in;

public IOBlocked(InputStream in) {
this.in = in;
}
@Override
public void run() {
try {
System.out.println("waiting for read");
in.read();
} catch (IOException e) {
if (Thread.currentThread().isInterrupted()) {
System.out.println("Interrupted from blocked I/O");
}
}
System.out.println("Existing IOBlocked.run()");
}
}

/**
* @description 同步阻塞
*/
class SynchronizedBlocked implements Runnable {

public SynchronizedBlocked() {
//构造方法中使用一个内部匿名thread获取这个线程的锁定
new Thread() {
@Override
public void run() {
f();
}
}.start();
}

//一旦获取后不再释放锁
public synchronized void f() {
while (true) {
Thread.yield();
}
}

@Override
public void run() {
System.out.println("trying to call f()");
f();
System.out.println("Existing SynchronizedBlocked.run()");
}
}
这三种阻塞分别是sleep阻塞,I/O读写阻塞,同步阻塞。

程序的输出:

Interrupting SleepBlocked

Interrupt sent to SleepBlocked

InterruptedException

Existing SleepBlock.run()

waiting for read

Interrupting IOBlocked

Interrupt sent to IOBlocked

trying to call f()

Interrupting SynchronizedBlocked

Interrupt sent to SynchronizedBlocked

aborting with system.exit(0)

证明了I/O和在synchronized块上的等待是不可中断的。而sleep的等待是可以中断的。一般来说,当可能阻塞的方法声明中有抛出InterruptedException则暗示该方法是可中断的。

ReentrantLock上阻塞的任务和synchronized块上的不同,具备被可以被中断的能力:

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

class BlockMutex {
private Lock lock = new ReentrantLock();
//构造时即获得线程锁,并不释放
public BlockMutex() {
lock.lock();
}

public void f() {
try {
lock.lockInterruptibly();
System.out.println("在f中请求锁");
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println("在f中请求锁时发生中断");
}
}
}

class Blocked implements Runnable {
BlockMutex blockMutex = new BlockMutex();

@Override
public void run() {
System.out.println("等待");
blockMutex.f();
System.out.println("被打断");
}
}

public class Interrupting {
public static void main(String[] args) throws Exception {
Thread thread = new Thread(new Blocked());
thread.start();
TimeUnit.SECONDS.sleep(1);
thread.interrupt();
}
}
输出结果:

等待

java.lang.InterruptedException

在f中请求锁时发生中断

被打断

at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireInterruptibly(AbstractQueuedSynchronizer.java:896)

at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1221)

at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:340)

at BlockMutex.f(Interrupting.java:14)

at Blocked.run(Interrupting.java:29)

at java.lang.Thread.run(Thread.java:722)

可以看到,由于在构造时获得了线程锁,如果f是synchronized的,另一个线程调用f时会一直处于阻塞状态并且不能中断,lock.lockInterruptibly();则允许在阻塞时发生中断。

中断的使用场景有如下几个:

某个操作超过了一定的执行时间限制需要中止时;

多个线程做相同的事情,只要一个线程成功其它线程都可以取消时;

一组线程中的一个或多个出现错误导致整组都无法继续时;

当一个应用或服务需要停止时。

参考:http://www.infoq.com/cn/articles/java-interrupt-mechanism
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: