前言:
此时朋友们对“java程序退出”大体比较关心,你们都需要分析一些“java程序退出”的相关知识。那么小编也在网摘上网罗了一些有关“java程序退出””的相关文章,希望兄弟们能喜欢,大家快快来学习一下吧!这是并发编程系列的第七篇文章。上一篇介绍了Executor框架和线程池相关的内容,这一篇说一下任务的取消及关闭
俗话说得好,知道如何起飞很重要,但是知道如何平稳落地更重要。线程和任务的启动我们已经很清楚了,但是如何做到线程及任务安全的关闭及取消,也是有一定技巧的。
取消任务
大部分场景下,启动的任务都会按照计划正常的运行,然后结束。但是有些场景我们必须提前结束任务。比如
用户发送了取消请求。超过了某个规定的时间。发生了某种错误。应用程序被关闭。
Java里面取消任务的执行一般有两种方法。
第一种方法,设置一个标志位,任务执行过程会检查该标志位,如果为true则结束任务。该方法对于执行阻塞调用的情况下会有缺陷,下面会详细说
第二种方法,通过中断来安全的结束任务。
标志位
假设有一个产生偶数的生成器,我们希望通过设置一个标志位来结束该任务,否则这个任务会一直运行下去。
定义一个偶数生成任务,示例代码如下
public class EvenGenerateTask implements Runnable{ private int count = 0; //取消任务标志位 private volatile boolean canceled = false; public synchronized int nextEven(){ count += 2; return count; } @Override public void run() { //通过标志位判断任务是否运行 while (!canceled){ count = nextEven(); System.out.println(count); } } public void stop(){ this.canceled = true; }}
启动任务,并通过标志位结束该任务。
public static void main(String[] args) throws InterruptedException { EvenGenerateTask evenGenerateTask = new EvenGenerateTask(); Thread thread = new Thread(evenGenerateTask); //启动偶数生成任务 thread.start(); //运行一秒钟 Thread.sleep(1000); //停止偶数生成任务 evenGenerateTask.stop();}
上面的代码,可以安全的实现任务的取消操作,但是对于有阻塞方法调用的时候,可能情况就不同了。
假设将产生的偶数放到一个BlockQueue里面。来模拟一个针对偶数的生产者和消费者模式。
例如我们的代码写成这样
public void run() { int result = nextEven(); while (!canceled){ try { //任务有可能阻塞在此处,一直看不到canceled的值 //也就是任务永远不会停止,除非接触阻塞。 blockingQueue.put(result); } catch (InterruptedException e) { e.printStackTrace(); } }}
上面的代码,put操作是个阻塞操作,如果队列满了,那么任务将会一直处于阻塞状态,根本不会检查取消标志位,所以无论我们是否调用了取消方法,都没办法取消任务了。
中断
对于上面的场景,一般通过中断的方式来安全的取消任务。
每个线程都有一个表示线程中断状态的标志位,当线程处于中断状态时,该标志位为true。此外每个线程还具有如下三个与中断相关的方法。
interrupt:该方法能够中断目标线程,并设置线程的中断状态。 isInterrupted:该方法会返回一个线程的中断状态。 interrupted:该方法可以清除当前线程的中断状态,并返回当前的值。
注意:调用interrupt方法时,只是向目标线程发送了中断的请求,并不代表线程会立刻中断正在执行的任务,也就是是否中断看心情。但是大部分都会立刻中断。
示例,通过中断取消偶数生成,核心代码如下
@Overridepublic void run() { int result = nextEven(); //通过线程的中断状态,来决定是否退出任务 while (!Thread.currentThread().isInterrupted()){ try { blockingQueue.put(result); } catch (InterruptedException e) { e.printStackTrace(); } }}public void cancel(){ //发送中断请求 interrupt();}
在上一篇文章中,我们推荐使用Executor的方式执行任务,不建议大家要使用Thread的方式。如果使用Executor的方式启动任务,(屏蔽了Thread的概念)该如何终止任务呢?答案就是Future。
Future提供了一个cancle方法,该方法接受一个boolean类型的参数,如果为true,且任务正在被某个线程执行,那么这个线程可以被中断。如果为false,表示任务如果没有启动,那么就不要启动了。
示例代码如下:
ExecutorService exec = Executors.newCachedThreadPool();Future<?> task = exec.submit(evenGenerateTask);try { task.get(5, TimeUnit.SECONDS);} catch (ExecutionException | TimeoutException e) { //可以重新抛出异常}finally { //超时没有返回的任务将被中断 task.cancel(true);}
上面的内容介绍的是能够响应中断的阻塞操作,还有一种阻塞线程是不能响应中断,比如I/O操作的read()和write()方法,比如在等待获取某个内置锁的时候,也是无法响应中断的。对于这种情况,我们可以通过直接关闭底层资源的方式来结束任务。
结束
这篇文章介绍了如何安全的取消和关闭任务,虽然中断并不具有取消的含义,但是我们还是会通过响应中断的方式来安全的终止任务。下一篇介绍一下死锁的问题。
标签: #java程序退出 #java文件提前结束