一个JAVA多线程的问题 来挑战下?

bryanbj 发布于 2014/01/17 18:28
阅读 555
收藏 1
下面的代码是否线程安全? 给出详细理由 谢谢~

 
 
 

 

 
 
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;
    }
}

}



加载中
0
优雅先生
优雅先生

@刘禹星 说的是对的。你上面使用的synchronized是对象内置监控锁,如果所有其他方法中对list的访问都要先获取这个对象内置监控锁的话,就是线程安全的。否则是非线程安全的。这里其实有两个锁,一个是putIfAbsent方法所属对象的内置锁。另外一个是访问list时加的锁(对整个list加锁,通过synchronizedXX(...)就给原始的集合对象增加了这么一个“锁”的包装),这样可以使单独的add、contains操作是线程安全的,但是两个线程安全的操作合起来就不一定线程安全,因为后一个操作依赖于前一个操作的执行结果。如果要强制保证线程安全的话可以这样修改:

synchronzied(list) {
   boolean absent = !list.contains(x);
   if (absent)
      list.add(x);
}

这样你不需要用synchronized修饰putIfAbsent都可以保证对list的contains和add操作被合成了一个”对list的原子操作”。

你上面用synchronized修饰putIfAbsent保证的是这个方法是同步方法,但不能保证contains和add操作合起来具有”原子性“。因为可能有另外的非同步方法在contains和add两个操作发生时间的中间某一刻对list进行查找、修改、增加或者删除。

更具体的可以参考:http://www.ibm.com/developerworks/cn/java/j-jtp07233 以及《Java并发编程实战》


ArrayListt
ArrayListt
意思就是说。Collections.synchronizedList 。你传入的是什么,就锁的是什么?
0
linker
linker

不安全吧

list是public,在初始化时就可能被外部访问到

0
ArrayListt
ArrayListt

关键点在于 Collections.synchronizedList

和synchronized  的lock是不是lock的同一个对象

如果是,应该是安全的,如果不是,就不是安全的

0
专业打酱油
专业打酱油
- -!,安全。。。最起码list也是private的吧。。。。
0
dreamers
dreamers

这个要看用的情况是不是安全的。如果只是进行 putIfAbsent 的操作就是安全的,如果进行其他的操作就不安全了,比如将 list 指向了其他对象,而其他线程又会得到不一样的结果。这时就会造成线程不安全。

如果将 public List<E> list = Collections.synchronizedList(new ArrayList<E>());

改为

 public final List<E> list = Collections.synchronizedList(new ArrayList<E>());

应该就安全了。

0
dreamers
dreamers
不知道我的理解正不正确,期待大牛指点下。
0
YuKunYi
YuKunYi
就这些代码看是安全的,但是用起来就不一定了,list可能被任意访问设置,也能被删除元素,要是其他地方移除元素putIfAbsent也不安全……
0
typeorigin
typeorigin
这个只能叫相对线程安全。只能保证同一时刻有一个线程访问List,比如如果有下面的代码
for(int i = 0; i < list.size; i++) {
  list.remove(i)
}

这段代码由两个线程执行, 比如线程1, 线程2

那么没有办法保证线程2访问list的时候 线程1 没有对list的size进行修改,synchronized只表示对list自己的操作比如add,这些操作的不需要我们同步。但是像上面例子中非原子(size() 方法和 remove() 方法)的操作需要外部同步

synchronized(list) {
   for(int i = 0; i < list.size(); i++) {
      list.remove(i);
   }
}



这样才能保证访问时安全的



0
281165273
281165273
这是java并发编程实战这本书上的一个反例,通过synchronizedlist发布应该是安全的,但是list的其他操作使用的list这个引用指向的对象的锁,而putifabsent方法拿到的是listhelp当前对象的锁,所以putifabsent方法对于list的其他方法并不是原子操作。修改的方法,去掉putifabsent的sync关键字,在putifabsent方法里面用synchronized(list)做同步。
0
华兹格
华兹格

synchronized 修饰的是一个方法,方法里含有实例变量,所以这是线程安全的!

参考:http://www.cnblogs.com/GnagWang/archive/2011/02/27/1966606.html

返回顶部
顶部