任务取消
场景
- 用户手动取消。
- 线程超时。
- 线程提前结束,已经得到结果。
- 发生错误。
- 主线程关闭。
- java没有抢占式的线程停止方式,必须依靠线程本身与外部状态通过协作式的方式停止。
线程中断
- 通过设置状态位,并在某些方法中去检查这个状态位,但是这些可以响应中断状态的方法,可能被阻塞,例如调用BlockingQueue的put方法。
中断方法
- 每个线程都由一个静态的中断状态位,
interrupt
。 interrupt()
中断线程,但是不清除状态位。interrupted()
清除当前中断状态,并返回之前的值。
一般说来,当可能阻塞的方法声明中有抛出InterruptedException则暗示该方法是可中断的,如BlockingQueue#put、BlockingQueue#take、Object#wait、Thread#sleep等
中断策略
- 抛出
InterruptedException
,来通知上一层的代码。 - 恢复中断状态使上一层代码可以检查到。
通过Future来实现取消
Future<?>task=taskExec.submit(r);
try{
task.get(timeout,unit);
}
catch (TimeoutExceptione){
//下面任务会被取消
}
catch (ExecutionExceptione){
//task中抛出的异常;重抛出
throw launderThrowable(e.getCause());
}
finally{
//如果任务已经结束,是无害的
task.cancerl(true);
}
}
处理不可中断的阻塞
- 不响应中断的方法
- Socket I/O ,read和write方法,可以通过强行关闭Socket中断。
- 同步I/O
- Selector的异步I/O,如果调用select方法的时候阻塞了,可以通过调用close方法或wakeup方法,使
其抛出ClosedSelectorException - 获取一个锁的时候,也不会响应中断,但是Lock的lockInterruptibly,弥补了这些不足,它允许等待锁的同时,可以响应中断。
- 通过重写Thread的interrupt方法来解决上述的不响应中断的类的中断问题。
package net.jcip.examples;
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
/**
* ReaderThread
* <p/>
* Encapsulating nonstandard cancellation in a Thread by overriding interrupt
*
* @author Brian Goetz and Tim Peierls
*/
public class ReaderThread extends Thread {
private static final int BUFSZ = 512;
private final Socket socket;
private final InputStream in;
public ReaderThread(Socket socket) throws IOException {
this.socket = socket;
this.in = socket.getInputStream();
}
public void interrupt() {
try {
socket.close();
} catch (IOException ignored) {
} finally {
super.interrupt();
}
}
public void run() {
try {
byte[] buf = new byte[BUFSZ];
while (true) {
int count = in.read(buf);
if (count < 0)
break;
else if (count > 0)
processBuffer(buf, count);
}
} catch (IOException e) { /* Allow thread to exit */
}
}
public void processBuffer(byte[] buf, int count) {
}
}
通过newTaskFor来封装非标准的取消
- 讲的是 如何扩展Future的cancel方法。
package net.jcip.examples;
import java.io.IOException;
import java.net.Socket;
import java.util.concurrent.*;
import net.jcip.annotations.*;
/**
* SocketUsingTask
* <p/>
* Encapsulating nonstandard cancellation in a task with newTaskFor
*
* @author Brian Goetz and Tim Peierls
*/
public abstract class SocketUsingTask <T> implements CancellableTask<T> {
@GuardedBy("this") private Socket socket;
protected synchronized void setSocket(Socket s) {
socket = s;
}
public synchronized void cancel() {
try {
if (socket != null)
socket.close();
} catch (IOException ignored) {
}
}
public RunnableFuture<T> newTask() {
return new FutureTask<T>(this) {
public boolean cancel(boolean mayInterruptIfRunning) {
try {
SocketUsingTask.this.cancel();
} finally {
return super.cancel(mayInterruptIfRunning);
}
}
};
}
}
interface CancellableTask <T> extends Callable<T> {
void cancel();
RunnableFuture<T> newTask();
}
@ThreadSafe
class CancellingExecutor extends ThreadPoolExecutor {
public CancellingExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
}
public CancellingExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory);
}
public CancellingExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler);
}
public CancellingExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
}
protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
if (callable instanceof CancellableTask)
return ((CancellableTask<T>) callable).newTask();
else
return super.newTaskFor(callable);
}
}