我正在尝试提高对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);
}
}
删除这个的副作用是什么?
答案 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
不是线程安全的。
经验法则是:对共享可变状态的每次访问(读或写)必须在同一个锁上同步。