了解Synchronized的行为

我正在努力提高对synchronized调用期间发出的锁定范围的理解。

例如:

 class CopyOnReadList { private final List items = new ArrayList(); public void add(T item) { items.add(item); } public List makeSnapshot() { List copy = new ArrayList(); synchronized (items) { // Make a copy while holding the lock. for (T t : items) copy.add(t); } return copy; } } 

(代码精心借鉴了这个优秀的答案 )

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

原帖实际上在add方法中使用了synchonized锁:

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

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

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

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

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

在这段代码片段中,一个线程可以调用add而另一个调用makeSnapshot吗?

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

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

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

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

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

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

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