在书中有一个实例,恕我愚钝,不明白这样设计的意图何在:
(例子的背景是:机动车位置追踪)
public class SafePoint{
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;
}书中说:私有的构造方法的存在可以避免一些竞争条件,这些竞争条件会发生在赋值构造函数实现为this(p.x,p.y)的时候,这是私有构造函数捕获模式的实例。问题1:这句话到底是什么什么意思?既然调用构造方法,一定是产生不同的对象实例,为什么还会存在竞争?public class Publishing{
 private final Map<String,SafePoint> locations;
 private final Map<String,SafePoint> unmodifiableMap;
 
 public Publish(Map<String,SafePoint> locations)
 {
   this.locations=new ConcurrentHashMap<String,SafePoint>(locations);
   this.unmodifiableMap=Collections.unmodifiableMap(this.locations);
 }
 public Map<String,SafePoint> getLocations()
 {
   return unmodifiableMap;
 }
 public SafePoint getLocation(String id)
 {
   return locations.get(id);
 }
 ....................
}问题2:这里既然locations是ConcurrentHashMap类型,而且是final的,为什么还要将它用Collections.unmodifiableMap(this.locations) 这个包装,为什么不直接返回locations?(当调用public Map<String,SafePoint> getLocations())
 unmodifiableMap(this.locations)也是维护的一个final类型的Map。
 因为这是大师给的例子,我想一定有他的原因,不是随便弄几个没用的例子上来,所以希望大虾帮我解答一下,谢谢了!

解决方案 »

  1.   

    对于问题1:
    public SafePoint(SafePoint p)
    {
      this(p.get())
    }
    是一个典型的拷贝构造函数,它为什么没有实现为:
    public SafePoint(SafePoint p)
    {
      this(p.x,p.y)
    }是因为这种实现可能会有多线程竞争的风险,因为this(p.x,p.y)由两个赋值语句构成: this.x=x;和this.y=y;如果在执行了第一个语句之后,将要执行第二个语句之时,p的y值被另外一个线程修改了,就会导致对象状态不统一的情况,从而导致拷贝构造函数逻辑失败,所以存在竞争的风险。
    而改用get方法则没有此风险的原因是因为get()方法是用synchronized修饰的,锁在对象上的,保证了对象状态的一致性。对于问题2:
    为什么要用Collections的unmodifiableMap方法返回一个集合的引用,其实这里跟多线程没有关系,这属于一个良好的集合封装范例:方法不应该直接返回一个集合的引用,而应该返回该集合的不可修改的引用,如果调用者想修改该集合,需要创建另外的方法来修改之。
    <重构改善既有代码的设计>一书一节详细阐述了该重构手段,叫:封装集合
      

  2.   

    谢谢你的回复,我还有一个问题想请教你,直接上代码:
    public class Test { public List<String> lists = Collections
    .synchronizedList(new ArrayList<String>());    public synchronized boolean listIfAbsent(String str) {
    boolean ifAbsent = lists.contains(str);
    if (!ifAbsent) {
    lists.add(str);
    }
    return ifAbsent;
    }
        
    public boolean listIfAbsent1(String str) {
    synchronized (lists) {
    boolean ifAbsent = lists.contains(str);
    if (!ifAbsent) {
    lists.add(str);
    }
    return ifAbsent;
    } } public static void main(String[] args) {
    final Test t = new Test();
                          new Thread() {
    public void run() {
    //该线程正在对list最修改
    if (!t.lists.contains("a")) {
    t.lists.add("a");
    }
    if (!t.lists.contains("b")) {
    t.lists.add("b");
    }
    // t.lists.set(0, "chenliang");
    System.out.println(t.lists);
    }
    }.start(); t.listIfAbsent1("a");
    // t.listIfAbsent("b"); }
    }我就是不明白,直接对lists加锁,和对整个this加锁的区别,this的组成不就是由多个成员变量组成吗?
    对this加锁不就是保证了他的数据域不被破坏吗?可是不是这样的,为什么直接锁this和锁成员变量他们用的不是一把锁?
    修改的线程和主线程都是操作的一个对象,也就是我标红的部分。为什么会有这样的差异?想不通
      

  3.   

    直接对lists加锁可以保证其他线程结构修改了该lists的不会引发同步问题如果直接对this加锁,其他线程如果直接操作lists并修改之会引发同步问题(因为你这个lists对象是public的,其他线程可以直接访问)
      

  4.   

    咳咳,楼主其实没有理解java里锁的概念究竟是什么。java里的每个对象都有个lock属性,比如一个object都有个lock, 和数据库一样,锁只是一个对象的属性。this是针对你new出对象的引用,所以是在你新建对象Test上加锁,和这个对象Test里面的属性一点关系都没有。当线程访问这个你Test对象的时候,就是去查这个Test对象lock有没有被占用了。不会去查里面的lists有没有被占用。