深刻浅出 Java Concurrency (36): 线程池 part 9 并发操做异常体系[转]

并发包引入的工具类不少方法都会抛出必定的异常,这些异常描述了任务在线程池中执行时发生的例外状况,而一般这些例外须要应用程序进行捕捉和处理。html

例如在Future接口中有以下一个API:java

 

java.util.concurrent.Future.get(long, TimeUnit) throws InterruptedException, ExecutionException, TimeoutException;

 

前面的章节中描述了Future类的具体实现原理。这里再也不讨论,可是比较好奇的抛出的三个异常。网络

这里有一篇文章(Java 理论与实践: 处理 InterruptedException)描述了InterruptedException的来源和处理方式。简单的说就是线程在执行的过程当中被本身或者别人中断了。这时候为了响应中断就须要处理当前的异常。并发

对于java.lang.Thread而言,InterruptedException也是一个很诡异的问题。工具

中断一个线程Thread.interrupt()时会触发下面一种状况:测试

若是线程在调用 Object 类的 wait()、wait(long) 或 wait(long, int) 方法,或者该类的 join()、join(long)、join(long, int)、sleep(long) 或 sleep(long, int) 方法过程当中受阻,则其中断状态将被清除,它还将收到一个 InterruptedException。ui

检测一个线程的中断状态描述是这样的Thread.interrupted():.net

测试当前线程是否已经中断。线程的中断状态 由该方法清除。换句话说,若是连续两次调用该方法,则第二次调用将返回 false(在第一次调用已清除了其中断状态以后,且第二次调用检验完中断状态前,当前线程再次中断的状况除外)。 线程

也就是说若是检测到一个线程已经被中断了,那么线程的使用方(挂起、等待或者正在执行)都将应该获得一个中断异常,同时将会清除异常中断状态。htm

 

V innerGet(long nanosTimeout) throws InterruptedException, ExecutionException, TimeoutException {
    if (!tryAcquireSharedNanos(0, nanosTimeout))
        throw new TimeoutException();
    if (getState() == CANCELLED)
        throw new CancellationException();
    if (exception != null)
        throw new ExecutionException(exception);
    return result;
}

 

上面获取任务结果的方法实现中,将在获取锁的过程当中获得一个中断异常。代码java.util.concurrent.locks.AbstractQueuedSynchronizer.tryAcquireSharedNanos(int, long)描述了这种状况:

    public final boolean tryAcquireSharedNanos(int arg, long nanosTimeout) throws InterruptedException {
    if (Thread.interrupted())
        throw new InterruptedException();
    return tryAcquireShared(arg) >= 0 ||
        doAcquireSharedNanos(arg, nanosTimeout);
    }

 

 

这里在获取锁的时候检测线程中断状况,若是被中断则清除中断位,同时抛出一个中断异常。为何如此作?由于咱们的线程在线程池中是被重复执行的,因此一旦线程被中断后并不会退出线程,而是设置中断位,等候任务队列本身处理线程,从而达到线程被重复利用的目的。有兴趣的能够参考代码java.util.concurrent.ThreadPoolExecutor.Worker.runTask(Runnable)。这里在关闭线程池时就会致使中断全部线程。

除了InterruptedException 异常咱们还发现了一个全新的异常java.util.concurrent.TimeoutException,此异常是用来描述任务执行时间超过了指望等待时间,也许是一直没有获取到锁,也许是尚未执行完成。

在innerGet代码片断中咱们看到,若是线程在指定的时间没法获取到锁,那么就会获得一个超时异常。这个很好理解,好比若是执行一个很是耗时的网络任务,咱们不但愿任务一直等待从而占用大量的资源,可能在必定时间后就会但愿取消此操做。此时超时异常很好的描述了这种需求。

与此同时,若是取消了一个任务,那么再次从任务中获取执行结果,那么将会获得一个任务被取消的异常java.util.concurrent.CancellationException。

除了上述异常外,还将获得一个java.util.concurrent.ExecutionException异常,

这是由于咱们的提交的任务java.util.concurrent.Callable在call()方法中容许抛出任何异常,另外常规的线程执行也可能抛出一个RuntimeException,因此这里简单包装了下全部异常,看成执行过程当中发生的异常ExecutionException抛出。

以上就是整个异常体系,全部并发操做的异常均可以归结于上述几类。

不少状况下处理时间长度都是用java.util.concurrent.TimeUnit,这是一个枚举类型,用来描述时间长度。其中内置了一些长度的单位。其中包括纳秒、微秒、毫秒、秒、分、时、天。例如超时操做5秒,可使用

Future.get(5,TimeUnit.SECONDS) 或者 Future.get(5000L,TimeUnit.MILLISECONDS)

固然一种单位的时间转换成另外一种单位的时间也是很是方便的。另外还有线程的sleep/join以及对象的wait操做的便捷操做。

相关文章
相关标签/搜索