3
回答
通过Collections.synchronizedList获取安全的list后,为何还要用synchronized修饰?

官方文档就是如此使用的:

List list = Collections.synchronizedList(new ArrayList());
      ...
  synchronized (list) {
      Iterator i = list.iterator(); // Must be in synchronized block
      while (i.hasNext())
          foo(i.next());
  }

看了下源码,其实就是套了一层,把list的操作方法用sychronized裹起来了,如:

 public void add(int index, E element) {
            synchronized (mutex) {list.add(index, element);}
        }
        public E remove(int index) {
            synchronized (mutex) {return list.remove(index);}
        }

而mutex则是父类SynchronizedCollection中的this对象

有两个问题想不明白:

1. 既然封装类内部已经加了对象锁,为什么外部还要加一层对象锁?

2. 第一段代码和下面的代码有什么区别?

List list = new ArrayList();
      ...
  synchronized (list) {
      Iterator i = list.iterator(); // Must be in synchronized block
      while (i.hasNext())
          foo(i.next());
  }
 

有点想不通……

<无标签>
举报
itwriter
发帖于2年前 3回/1K+阅

先说基本原理:

synchronized(obj)将obj视为一个锁,执行该方法块的线程需要获取obj这个锁。但这个操作仅仅表示我需要obj这锁,并不代表我不能同时执行obj.a()与obj.b()。

然后说第一个问题,外部锁其实就是为了list.iterator()在读取过程中,不会本来hasNext()有的,但在调用i.next()的时候,另外一个线程把它删了,这个synchronized块是为了保障这三行代码在多个线程里同时执行的并发问题

至于内部锁,那是在并发执行add/remove的时候,不要把多个线程的东西加到list内部实现的同一个位置上去,导致数据丢失或者脏数据等问题,这是为了保证这个List在执行add/remove时不会存在并发问题。简而言之,这两个锁是不同层面上的并发问题。

再来说第2个问题,你ArrayList能解决外部锁的问题,你解决不了内部锁解决的问题,这也是Collections.synchronizedList并发类存在的原因。

 

 

--- 共有 2 条评论 ---
叫我哀木涕 回复 @itwriter : 有点赞同第二个解释没啥问题,iterator不是安全的。所以在遍历的时候加上synchronized。而创建list的时候用Collections.synchronizedList,应该是为了初始化的并发状况造成的不同步访问或者多次初始化的问题。 12个月前 回复
itwriter赞同对第一点的回答。对于第二点,有异议。 从代码看来,我认为里边的锁和外边的锁是一样的,因为锁都是list对象本身,没有什么外部锁、内部锁的区别。 刚看了一片文章后(我在下边的回答附上链接了),感觉Collections.synchronizedList存在纯粹是为了让我们少写点sychronized代码块而已。 2年前 回复

已经找到相关文章了。

第一个问题:虽然被包装过的list是线程安全的,但返回的list获得的Iterator仍然是线程不安全的,所以仍然要用Synchronized代码块包裹起来。

至于第二个问题,其实上面的文章链接也有提到, 大意是:与其所有(多线程)要用到该list都加上Synchronized修饰,还不如直接使用安全的collection更聪明。

也就是说,如果不使用Iterator迭代器的话,使用了安全的collection以后,其实我们不需要再用Synchronized代码块修饰了,这样使用上更方便。

反过来说,使用Iterator迭代器的话,似乎也没必要用Collections.synchronizedList的方法来包装了——反正都是必须要使用Synchronized代码块包起来的。

所以总的来说,Collections.synchronizedXX这种做法,适合不需要使用Iterator、对性能要求也不高的情况。

 

顶部