Java Concurrency in Practice的中文版,虽然翻译过来的书名很土,但内容还是很不错的,豆瓣9+。

  1. 取消与关闭
    1. 取消策略,实质是一种线程间单工通信的策略,除了要定义发送取消请求的协议(how,例如调用自定义的函数或thread.interrupt()),还需要定义被取消线程如何处理取消请求,包括处理的时刻(when,例如何时检测中断状态改变),以及处理的方式(what,例如推迟处理、释放资源、暂停或重启服务)。具体地,基于Java的线程中断机制定义的取消策略,可以称为中断策略。
    2. Java的线程中断策略
      1. 中断实际上并未在规范中与取消关联起来,但在实践中,中断是实现取消最合理的方式,并不适宜于其他语义相关联。
      2. thread.interrupt() 只是设置线程的中断状态,并不会实际停止线程或做其他的工作。
      3. Thread.interrupted() 返回且清除中断状态
      4. Java未提供抢占式中断机制,这是为了便于开发者在响应性与健壮性间取得平衡
    3. 线程与任务 除非线程专门执行某个特定的任务(例如Java中的Runnable),否则在中断策略中需要对这两个角色做出区分,例如线程池中的可复用线程与任务。当任务调用阻塞库方法收到中断请求时,在处理后应向上层传递InterruptedException(更严谨地,还需重新设置中断状态),以便线程做出处理,而不应屏蔽中断请求和对线程的中断策略做出任何假设(中断请求发送者也应如此)。线程只应由其所有者关闭,例如定义shutdown等方法。因此除非你知道关闭某线程的含义,否则不应关闭该线程。
    4. Java中的实例
      1. 利用Future取消任务 future.cancel(boolean mayInterruptIfRunning),参数若为true则中断执行任务的线程
      2. 阻塞库方法
        1. 可中断的阻塞库方法 例如Thread.sleep()、Object.wait()等,会在执行前检测线程的中断状态,若被设置,则立刻清除中断状态,并抛出InterruptedException。这是一种很合理的策略,将控制权迅速转移给阻塞库方法的调用者。
        2. 不可中断的阻塞库方法 需要自行中断线程的阻塞状态
          1. 同步Socket I/O:socket.close()
          2. InterruptibleChannel:中断导致ClosedByInterruptException,关闭导致AsynchronousCloseException
          3. Selector:调用close或wakeup导致ClosedSelectorException
          4. 内置锁无法中断,显式锁(Lock)的lockInterruptibly()方法支持响应中断
      3. 停止基于线程的服务
        1. 原则
          1. 除非拥有某线程,否则不应对其进行操控,例如中断、更改优先级等
          2. 线程所有权不可传递,例如某应用持有服务,服务持有线程,但应用并没有该线程的所有权,即无权作出中断线程等操作,但可以对服务作出对应的操作,例如关闭等
          3. 对于生命周期大于其持有线程的服务,应提供对应的生命周期方法
        2. ExecutorService
          1. shutdownNow():不同于shutdown(),返回所有还未被执行的任务
          2. awaitTermination(long timeout, TimeUnit unit):等待所有任务结束
        3. 处理异常的线程终止
          1. 手写try-catch捕获所有异常
          2. thread.setUncaughtExceptionHandler()
          3. Thread. setDefaultUncaughtExceptionHandler():对于大型服务常用该方法
          4. executorService.execute():抛出异常会调用UncaughtExceptionHandler,但executorService.submit()中的异常会被封装到ExecutionException在future.get()中抛出
      4. 关闭JVM
        1. JVM关闭钩子
          1. runtime.addShutdownHook(Thread hook):用于执行清理等工作
          2. 不保证调用顺序
          3. 钩子与其他未被关闭的线程会并发执行,因此要小心避免死锁
          4. 当JVM被强行关闭时,关闭钩子不会执行
          5. 为了避免死锁等异常,可以所有服务使用同一个关闭钩子,在该钩子内串行执行操作
        2. 守护线程
          1. JVM启动时只有主线程是普通线程
          2. 新创建的线程继承父线程的守护状态
          3. 与普通线程的区别仅在于线程退出的时候:若此时剩下的线程都是守护线程,则JVM关闭,不执行finally和回卷栈
          4. 仅当无需finally进行回收清理等操作的场景下才使用守护线程
        3. 终结器(finalize)
          1. 实现正确的终结器非常困难,加上存在的不确定性与对对象回收产生的性能开销,应避免使用
          2. 使用finally代替,唯一的例外在于:需要被管理的对象持有通过本地方法获得的资源