《Java并发编程实战》 线程安全类的设计

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

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

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

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

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

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

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

《Java并发编程实战》 线程安全之可见性

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

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

《Java并发编程实战》 线程安全之原子性

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