2011-12-13 38 views
2

我试图提高对​​调用期间发出的锁定范围的理解。了解同步的行为

如:

class CopyOnReadList<T> { 

    private final List<T> items = new ArrayList<T>(); 

    public void add(T item) { 
     items.add(item); 
    } 

    public List<T> makeSnapshot() { 
     List<T> copy = new ArrayList<T>(); 
     synchronized (items) { 
      // Make a copy while holding the lock. 
      for (T t : items) copy.add(t); 
     } 
     return copy; 
    } 

} 

(代码精心来自this excellent answer借来的)

在此代码段,可以一个线程调用add而另一个叫makeSnapshot?即,由synchronized (items)创建的锁是否会影响所有尝试读取到items,或只有通过makeSnapshot()方法尝试的读取?

原岗位实际使用Add方法synchonized锁:

public void add(T item) { 
    synchronized (items) { 
     // Add item while holding the lock. 
     items.add(item); 
    } 
} 

什么是消除这种副作用?

+0

如果您从* add *方法中删除* synchronized *,那么当您调用* makeSnapshot *时,您根本无法保证其他线程添加的所有项目都具有“快照”。因此,据我可以看到,消除* synchronized *调用的副作用是现在代码被破坏了。除此之外,甚至有可能达到不一致的状态...... –

回答

2

它仅影响makeSnapshot()或更一般地说已经同步(项目)块的任何其他方法(这意味着它将尝试获取项目对象和块的锁定直到有可能)。

从add()方法中删除同步块的副作用是add()不会尝试同步项目对象,因此将允许并发修改,包括执行makeSnapshot()时的同时修改。

如果没有在add()中同步,您可以让其他线程将元素添加到项目集合中。

1

在这个代码片段,可以在一个线程调用添加另一种就是调用 makeSnapshot?

当然 - 并且其中一种方法可能会因ConcurrentModificationException失败,或者列表内容可能已损坏。

并通过同步(项目)中创建的锁影响所有试图 读取到的物品,或者那些仅通过makeSnapshot() 方法尝试?

都没有。该锁对对象items的行为没有任何影响,仅限于在其上同步的块或方法 - 即确保没有两个线程可以同时执行任何这些块或方法。

1

可以一个线程调用add而另一个调用makeSnapshot?

是的。​​确保任何其他线程无法输入同一个对象(本例中为CopyOnReadList)的另一个同步代码块。由于您没有同步add方法,因此即使一个线程正在执行makeSnapshot,多个线程也可以同时调用add

通过删除同步的add方法,您已使代码无线程安全,因为ArrayList不是线程安全的。

经验法则是:对共享可变状态的每次访问(读或写)必须在同一个锁上进行同步。