了解Synchronized的行为

时间:2011-12-13 09:07:53

标签: java concurrency

我正在尝试提高对synchronized来电期间发出的锁定范围的理解。

例如:

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

删除这个的副作用是什么?

3 个答案:

答案 0 :(得分:2)

它只影响那些在makeSnapshot()中尝试的那些,或者更一般地说,它影响已经同步(items)块的任何其他方法(这意味着它将尝试获取项目对象的锁定并阻塞直到可能)。

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

如果没有在add()中进行同步,则可以让其他线程在创建快照时向项集合中添加元素。

答案 1 :(得分:1)

  

在此代码段中,一个线程调用可以在另一个调用时添加   makeSnapshot?

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

  

由synchronized(items)创建的锁是否影响所有尝试   读取项目,或仅读取通过makeSnapshot()尝试的项目   方法

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

答案 2 :(得分:1)

  

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

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

通过删除add方法上的synchronized,您已使代码成为非线程安全的,因为ArrayList不是线程安全的。

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