一、任务的取消于关闭
1、中断Thread
1.每个线程都有一个boolean类型的中断状态。true则是中断状态中
interrupt:发出中断请求;isInterrupt:返回中断状态;interrupted:清除中断状态
2.JVM中的阻塞方法会检查线程中断状态,其响应方法为:清除中断状态,抛出InterruptedException异常,表示阻塞操作被中断结束 ;但JVM不保证阻塞方法何时检测到线程的中断状态
3.中断的理解:不会真正的中断一个正在运行的线程,而只是发出请求,具体的中断由任务自己处理
通过中断来取消线程通常是最好的方法
public class PrimeProducer extendsThread {private final BlockingQueuequeue;
PrimeProducer(BlockingQueuequeue) {this.queue =queue;
}public voidrun() {try{
BigInteger p=BigInteger.ONE;while (!Thread.currentThread().isInterrupted())
queue.put(p=p.nextProbablePrime());
}catch(InterruptedException consumed) {/*Allow thread to exit*/
//如果捕获到中断异常,则由线程自己退出
}
}public voidcancel() {
interrupt();
}
}
2、不可中断的阻塞的中断
如:Socket I/O操作,即使设置了中断请求,也不会中断,但是close 套接字,会使其抛出异常,达到中断效果;因此我们要重写中断方法
//自定义callable实现类
public abstract class SocketUsingTask implements CancellableTask{privateSocket socket;protected synchronized voidsetSocket(Socket s) {
socket=s;
}//取消方法
public synchronized voidcancel() {try{if (socket != null)
socket.close();
}catch(IOException ignored) {
}
}//新建实例的方法
public RunnableFuturenewTask() {return new FutureTask(this) {public boolean cancel(booleanmayInterruptIfRunning) {try{
SocketUsingTask.this.cancel();
}finally{return super.cancel(mayInterruptIfRunning);
}
}
};
}
}//自定义callable接口
interface CancellableTask extends Callable{voidcancel();
RunnableFuturenewTask();
}//自定义 执行池
class CancellingExecutor extendsThreadPoolExecutor {
......//通过改写newTaskFor 返回自己的Callable
protected RunnableFuture newTaskFor(Callablecallable) {if (callable instanceofCancellableTask)return ((CancellableTask) callable).newTask();else
return super.newTaskFor(callable);
}
}
3、通过自定义取消计时任务
private static final ScheduledExecutorService cancelExec = newScheduledThreadPool(1);/***
*@paramr 任务
*@paramtimeout 超时时间
*@paramunit TimeUnit
*@throwsInterruptedException*/
public static void timedRun(final Runnable r,long timeout, TimeUnit unit) throwsInterruptedException {class RethrowableTask implementsRunnable {//通过一个volatile变量,来存储线程是否异常
private volatileThrowable t;public voidrun() {try{
r.run();
}catch(Throwable t) {this.t =t;
}
}private voidrethrow() {if (t != null)throwlaunderThrowable(t);
}
}
RethrowableTask task= newRethrowableTask();final Thread taskThread = newThread(task);
taskThread.start();//延时timeout个unit单位后 执行线程中断
cancelExec.schedule(() ->taskThread.interrupt(), timeout, unit);//无论如何都等待;如果线程不响应中断,那么通过join等待任务线程timeout时间后 不再等待,回到调用者线程
taskThread.join(unit.toMillis(timeout));//如果 任务线程中有异常,则抛出
task.rethrow();
}
注意:依赖于join,任务超时join退出 和 任务正常join推出 无法进行判断
4、通过Futrue来实现取消计时任务
private static final ExecutorService taskExec =Executors.newCachedThreadPool();public static void timedRun(Runnable r,long timeout, TimeUnit unit) throwsInterruptedException {
Future> task =taskExec.submit(r);try{//通过Futrue.get(超时时间),捕获相应的异常来处理计时运行和取消任务
task.get(timeout, unit);
}catch(TimeoutException e) {//task will be cancelled below
} catch(ExecutionException e) {//exception thrown in task; rethrow
throwlaunderThrowable(e.getCause());
}finally{//Harmless if task already completed
task.cancel(true); //interrupt if running
}
}
二、停止基于线程的服务
1.通常,服务不能直接中断,造成服务数据丢失
2.线程池服务也不能直接中断
1、日志服务
标准的生产者,消费者模式
public classLogService {private final BlockingQueuequeue;private finalLoggerThread loggerThread;private finalPrintWriter writer;private booleanisShutdown;private intreservations;publicLogService(Writer writer) {this.queue = new LinkedBlockingQueue();this.loggerThread = newLoggerThread();this.writer = newPrintWriter(writer);
}public voidstart() {
loggerThread.start();
}public voidstop() {synchronized (this) {
isShutdown= true;
}
loggerThread.interrupt();//发出中断
}public void log(String msg) throwsInterruptedException {synchronized (this) {if(isShutdown){throw new IllegalStateException(/*...*/);
}++reservations; //保存的正确的在队列中的日志数量
}
queue.put(msg);//将日志放入队列
}private class LoggerThread extendsThread {public voidrun() {try{while (true) {try{synchronized (LogService.this) {if (isShutdown && reservations == 0) {break;
}
}
String msg=queue.take();synchronized (LogService.this) {--reservations;
}
writer.println(msg);
}catch (InterruptedException e) { /*retry*/
//捕获了中断请求,但为了将剩余日志输出,不做处理,直到计数器 == 0时,关闭
}
}
}finally{
writer.close();
}
}
}
}
2、ExecutorService中断
shutDown和shutDownNow
通常,将ExecetorService封装;如LogService,使其具有自己的生命周期方法
shutDownNow的局限性:不知道当前池中的线程状态,返回未开始的任务,但不能返回已开始未结束的任务
public class TrackingExecutor extendsAbstractExecutorService {private finalExecutorService exec;private final Set tasksCancelledAtShutdown =Collections.synchronizedSet(new HashSet());publicTrackingExecutor() {
exec=Executors.newSingleThreadExecutor();
}/*public TrackingExecutor(ExecutorService exec) {
this.exec = exec;
}*/
public voidshutdown() {
exec.shutdown();
}public ListshutdownNow() {returnexec.shutdownNow();
}public booleanisShutdown() {returnexec.isShutdown();
}public booleanisTerminated() {returnexec.isTerminated();
}public boolean awaitTermination(longtimeout, TimeUnit unit)throwsInterruptedException {returnexec.awaitTermination(timeout, unit);
}public ListgetCancelledTasks() {if (!exec.isTerminated())throw new IllegalStateException(/*...*/);return new ArrayList(tasksCancelledAtShutdown);
}public void execute(finalRunnable runnable) {
exec.execute(newRunnable() {public voidrun() {try{
runnable.run();
}finally{if(isShutdown()&&Thread.currentThread().isInterrupted())
tasksCancelledAtShutdown.add(runnable);
}
}
});
}
@Testpublic void test() throwsInterruptedException {
ExecutorService executorService=Executors.newSingleThreadExecutor();
TrackingExecutor trackingExecutor= newTrackingExecutor();
trackingExecutor.execute(newRunnable() {
@Overridepublic voidrun() {try{
Thread.sleep(2000);
System.err.println("123123");
}catch(InterruptedException e) {
Thread.currentThread().interrupt();//设置状态 或继续抛,在execute中处理
e.printStackTrace();
}finally{
}
}
});
List runnables =trackingExecutor.shutdownNow();
trackingExecutor.awaitTermination(10,TimeUnit.SECONDS);
List cancelledTasks =trackingExecutor.getCancelledTasks();
System.err.println(cancelledTasks.size());
}
}
三、处理非正常线程终止
1.未捕获的Exception导致的线程终止
1.手动处理未捕获的异常
2.通过Thread的API UncaughExceptionHandler,能检测出某个线程又遇见未捕获而导致异常终止
注意:默认是将异常的的堆栈信息 输出到控制台;自定义的Handler:implements Thread.UncaughExceptionHandler覆写方法
可以为每个线程设置,也可以设置一个全局的ThreadGroup
Thread.setUncaughtExceptionHandler/Thread.setDefaultUncaughtExceptionHandler
2.JVM退出、守护线程等