当前位置:编程学习 > JAVA >>

关于阅读<<java并发编程实践>>的问题

在书中有一个实例,恕我愚钝,不明白这样设计的意图何在:
(例子的背景是:机动车位置追踪)
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:
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方法返回一个集合的引用,其实这里跟多线程没有关系,这属于一个良好的集合封装范例:方法不应该直接返回一个集合的引用,而应该返回该集合的不可修改的引用,如果调用者想修改该集合,需要创建另外的方法来修改之。
<重构改善既有代码的设计>一书一节详细阐述了该重构手段,叫:封装集合
--------------------编程问答-------------------- 谢谢你的回复,我还有一个问题想请教你,直接上代码:


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和锁成员变量他们用的不是一把锁?
修改的线程和主线程都是操作的一个对象,也就是我标红的部分。为什么会有这样的差异?想不通

--------------------编程问答-------------------- 直接对lists加锁可以保证其他线程结构修改了该lists的不会引发同步问题

如果直接对this加锁,其他线程如果直接操作lists并修改之会引发同步问题(因为你这个lists对象是public的,其他线程可以直接访问) --------------------编程问答-------------------- 咳咳,楼主其实没有理解java里锁的概念究竟是什么。

java里的每个对象都有个lock属性,比如一个object都有个lock, 和数据库一样,锁只是一个对象的属性。

this是针对你new出对象的引用,所以是在你新建对象Test上加锁,和这个对象Test里面的属性一点关系都没有。

当线程访问这个你Test对象的时候,就是去查这个Test对象lock有没有被占用了。不会去查里面的lists有没有被占用。 --------------------编程问答-------------------- 嗯,谢谢你解开我的疑惑
补充:Java ,  Java SE
CopyRight © 2022 站长资源库 编程知识问答 zzzyk.com All Rights Reserved
部分文章来自网络,