任务的取消

如果外部代码能在某个操作正常完成之前将其置入完成状态,那么这个操作就可以称为可取消的,行为良好的软件都应该能很完善地处理失败、关闭和取消等过程。 一个可取消的任务必须拥有取消策略,在这个策略中定义:

  • 其他代码如何请求取消该任务;
  • 任务在何时检查是否已经请求了取消;
  • 在响应取消请求时应该执行哪些操作;

Java中并没有提供任何机制来安全地终止线程,但它提供了一种协作机制,即通过设置某个已请求取消的标志,然后任务执行过程中定期地查看该标志,如果设置了,那么任务提前结束

任务的执行

任务通常是一些抽象且离散的工作单元,通过把程序中的工作分解到多个任务中,可以简化程序的组织结构,提供一种自然的事务边界来优化错误恢复过程,并提供一种自然的并行工作结构来提升并发性。

在正常的负载下,服务器应用程序应该同时表现出良好的吞吐量快速的响应性 。程序提供商希望程序支持尽可能多的用户,从而降低每个用户的服务成本,而用户则希望获得尽快的响应。当负荷过载时,程序的性能应该是逐渐降低,而不是直接失败。

理想情况下,可以找出清晰的任务边界,各个任务之间是相互独立的,即任务不依赖于其他任务的状态、结果或边界效应。独立性有助于并发,因为如果存在足够多的处理资源,那么这些独立的任务就可以并行执行,这也为调度与负载均衡等的实现提供了更好的灵活性。

线程安全性的委托

委托是创建线程安全类的一个最有效策略:只需让现有的线程安全类管理所有的状态即可。Java类库包含丰富的并发构建模块,如线程安全容器以及各种用于协调多个相互协作的线程的同步工具类。

线程安全类的设计

在设计线程安全类的过程中,需要包含以下三个基本要素:

  1. 找出构成对象状态的所有变量;
  2. 找出约束状态变量的不变性条件;
  3. 建立对象状态的并发访问管理策略;

对象封装它拥有的状态,并拥有这些状态的所有权或者说控制权,它决定采用何种加锁策略来维持变量状态的完整性,然而,如果发布了某个可变对象的引用,那么就不再拥有独占的控制权,最多是共享控制权。

一般对于从构造器或者从方法中传递进来的对象,类并不拥有这些对象,除非这些方法是被专门设计来转移传递进来的对象的所有权。例如容器,容器通常表现出一种所有权分离的形式,其中容器拥有其自身的状态,而客户代码则拥有容器中各个对象的状态。

比如Servlet框架中的ServletContextServletContextServlet提供了类似于Map形式的对象容器服务,在ServletContext中可以通过名称来注册或获取应用程序的Attribute对象,但这些对象由应用程序拥有,ServletContext容器只是替应用程序保管它们。

ServletContext必须是线程安全的,因为它肯定会被多个线程同时访问。因此当调用setAttributegetAttribute时,Servlet不需要使用同步。而如果Attribute对象本身不是线程安全的,那么在getAttribute之后为防止多线程并发访问,访问者依然需要使用同步。

HttpSessionServletContext有类似的功能,但它可能更加严格,会要求保存的Attribute对象是线程安全的,因为Servlet容器可能与Web Application同时访问HttpSession中的对象,而且对于Session通常会有复制或持久化之类的操作,所以要求这些对象是线程安全的。

线程安全之可见性

要编写正确的并发程序,关键在于:在访问共享的可变状态时进行正确的管理。同步的另一个重要目的是内存可见性,我们不仅希望防止某个线程正在使用的对象状态被另一个线程同时修改,而且希望当一个线程修改了对象状态后,其他线程能够看到发生的状态变化。

.后面介绍Jvm内存模型时会提到java线程之间内存可见性的必要条件:满足Happens-Before规则

线程安全之原子性

如果多个线程访问某个类时,这个类始终都能表现正确的行为,则可以称这个类是线程安全的。所以一个对象是否要考虑线程安全问题,取决于它是否被多个线程访问,以及是否存在多种状态。反之如果一个对象是无状态的,既它不包含任何域,也不包含任何对其他类中域的引用,那么它一定是线程安全的。否则,一般会在类中会封装必要的同步机制来保证操作的原子性,以便保证类在被多线程访问时的线程安全。