java并发编程实战-第4章-对象的组合

java并发编程实战-第4章-对象的组合

(重点:组合模式)

4.3.5 重点:理解发布状态可变的车辆追踪器

p58 理解脚注:  如果将拷贝构造函数实现为this(p.x,p.y),那么会产生竞态 条件,而私有构造函数则可以避免这种竞态条件.这是私有构造函数捕获模式的一个实例

测试类

public class SafePointMain {

public static void main(String[] args) throws Exception {

    final SafePoint originalSafePoint = new SafePoint(1,1);

    //One Thread is trying to change this SafePoint

    new Thread(new Runnable() {

        @Override

        public void run() {

            originalSafePoint.setXY(2, 2);

            System.out.println("Original : " + originalSafePoint.toString());

        }

    }).start();

    //The other Thread is trying to create a copy. The copy, depending on the JVM, MUST be either (1,1) or (2,2)

    //depending on which Thread starts first, but it can not be (1,2) or (2,1) for example.

    new Thread(new Runnable() {

        @Override

        public void run() {

            SafePoint copySafePoint = new SafePoint(originalSafePoint);

            System.out.println("Copy : " + copySafePoint.toString());

        }

    }).start();

}

}

安全类:使用私有构造函数

@ThreadSafe

public class SafePoint {

    @GuardedBy("this") private int x, y;

    private SafePoint(int[] a) { this(a[0], a[1]); }

    public SafePoint(SafePoint p) { this(p.get()); }

    public SafePoint(int x, int y) {

        this.x = x;

        this.y = y;

    }

    public synchronized int[] get() {

        return new int[] { x, y };

    }

    public synchronized void set(int x, int y) {

        this.x = x;

        this.y = y;

    }

   

@Override

public String toString() {

return "("+x+","+y+")";

}

}

用SafePointMain测试的结果(真确)

如果不用私有构造函数,而直接使用下面类SafePoint2,

class SafePoint2 {

    private int x;

    private int y;

    public SafePoint(int x, int y){

        this.x = x;

        this.y = y;

    }

    public SafePoint(SafePoint safePoint){

        this(safePoint.x, safePoint.y);

    }

    public synchronized int[] getXY(){

        return new int[]{x,y};

    }

    public synchronized void setXY(int x, int y){

        this.x = x;

        //Simulate some resource intensive work that starts EXACTLY at this point, causing a small delay

        try {

            Thread.sleep(10 * 100);

        } catch (InterruptedException e) {

         e.printStackTrace();

        }

        this.y = y;

    }

    public String toString(){

      return Objects.toStringHelper(this.getClass()).add("X", x).add("Y", y).toString();

    }

}

则测试结果,:

Copy : (2,1)

Original : (2,2)

本质上,是写(setXY) 与读(构造函数中的 this(safePoint.x, safePoint.y);)不同步造成的,而构造函数不不能使用同步机制的

但是getXY方法是安全的,所以构造一个接受getXY参数的构造函数

private SafePoint(int [] xy){

    this(xy[0], xy[1]);

}

And then, the actual invokation:

public  SafePoint (SafePoint safePoint){

    this(safePoint.getXY());

}

Notice that the constructor is private, this is because we do not want to expose yet another public constructor and think again about the invariants of the class, thus we make it private - and only we can invoke it.

具体参考stackoverflow上Eugene的精彩的解答:http://stackoverflow.com/questions/12028925/private-constructor-to-avoid-race-condition/12029346

4.1 设计线程安全的类

    三个基本要素

    变量、约束状态变量的不变性条件、建立对象状态并发访问的管理策略

    同步策略定义了如何在不违背对象的不边条件或者后验条件的情况下对其状态的访问进行协同。

    同步策略规定了如何将不变性、线程封闭、加锁机制等结合起来维护线程的安全性。规定了哪些变量由哪写所保护,为了开发人员可以对这个类进行分析与维护,还得把同步策略写成文档

    

4.1.1 收集同步需求

      比如,

      Count类中的变量的long的取值范围、不能为负数

      当前状态时17,那么下一个状态必须是18的后验条件等

      

      例子:

      @ThreadSafe

public final class Counter {

   @GuardedBy("this")  private long value = 0;

   public synchronized  long getValue() {

       return value;

   }

   public  synchronized  long increment() {

       if (value == Long.MAX_VALUE)

           throw new IllegalStateException("counter overflow");

       return ++value;

   }

}

      

4.1.2 依赖状态的操作

    

    For example, you cannot remove an item from an empty queue; a queue must be in the "nonempty" state before you can remove an element.

    

    

4.1.3 状态所有权

     比如,servlet中的ServletContext中的对象通过setAttribut和getAttribut不需要同步,但其状态所有权属于应用程序,由应用程序来控制同步

4.2 实例封闭

    

    将数据封装在对象内部,对象的方位只限制在对象的方法上,从而更容易确保线程在访问数据时总能持有争取的锁

4.2.1  java监视器模式

     即:synchronized ,Vector 和Hashtable

     

4.2.2 示例:车辆追踪

      

     在车辆很多的情况下,如果需要一致的快照则是优点。如果需要实时的最新信息,则是缺点

     

4.3 线程安全性的委托

4.3.1 示例:基于委托的车辆追踪器

     使用不变的point类

     

     将线程安全委托给ConcurrentHashMap

     

     

4.3.2 独立的状态变量

     例子:VisualComponent keyListeners 和 mouseListeners 中两个链表都是线程安全的,且没有耦合。所以VisualComponent把安全委托给两个安全的链表是可以的

4.3.3 当委托无效时  

     例子:NumberRange 中  AtomicInteger lower 和  AtomicInteger upper,虽然是线程安全的类。但彼此有耦合关系,此时需要加锁机制处理 

4.3.4 发布底层的状态变量 

      如果Counter类中,value有约束不能为负数,发布value则会这个类出错

     VisualComponent中keyListeners 和 mouseListeners 时安全的,是可以发布的

      

4.3.5 示例:发布状态可变的车辆追踪器

     (已在开头重点描述)

     

     

4.4 在现有安全类中添加功能

   例如,给一个安全的列表添加原子操作(put-if-Absent)

   方法1:最安全的方式是修改源代码,但这通常无法做到    

   方法2:扩张extends,但比直接修改源代码脆弱。因为同步策略实现被分布到多个单独维护的线程中

   方法3:扩展类的功能(4.4.1 客户端加锁例子)

   方法4:组合(更好的方式,4.4.2介绍)

   

4.4.1 客户端加锁机制

  对于 被Collections.synchronizedLis的ArrayList,以上方法都不行,以为客户端并不知道ArrayList的class,第三种方法,扩展类的功能,放在一个辅助类中

  

   @NotThreadSafe

public class ListHelper<E> {

   public List<E> list =

       Collections.synchronizedList(new ArrayList<E>());

   ...

   public synchronized boolean putIfAbsent(E x) {

       boolean absent = !list.contains(x);

       if (absent)

           list.add(x);

       return absent;

   }

}

但是如上方法,在错误的锁上进行了同步,应为

@ThreadSafe

public class ListHelper<E> {

   public List<E> list =

       Collections.synchronizedList(new ArrayList<E>());

   ...

   public boolean putIfAbsent(E x) {

       synchronized (list)  {

           boolean absent = !list.contains(x);

           if (absent)

               list.add(x);

           return absent;

       }

   }

}

4.4.2 Composition

Listing 4.16. Implementing Put-if-absent Using Composition.

@ThreadSafe

public class ImprovedList<T> implements List<T> {

    private final List<T> list;

    public ImprovedList(List<T> list) { this.list = list; }

    public synchronized boolean putIfAbsent(T x) {

        boolean contains = list.contains(x);

        if (contains)

            list.add(x);

        return !contains;

    }

    public synchronized void clear() { list.clear(); }

    // ... similarly delegate other List methods

}

4.5 将同步策略文档化

     Document a class's thread safety guarantees for its clients; 向客户端说明类的线程安全性保证的

     document its synchonization policy for its maintainers. 向维护人员提供同步策略

     

   

猜你喜欢

转载自zhouchaofei2010.iteye.com/blog/2243278