如果同步的代码块包含未同步的集合。集合是否被认为是线程安全的?如果没有,您能否提供两个线程可能不安全地访问同步代码中的集合的任何实际场景?
谢谢。
如果同步的代码块包含未同步的集合。集合是否被认为是线程安全的?如果没有,您能否提供两个线程可能不安全地访问同步代码中的集合的任何实际场景?
谢谢。
这是一个非常简单的对象,一个无界队列,我在这里的另一个示例中使用了它:
public final class MyQueue<T> {
private List<T> list = new ArrayList<T>();
public T take() {
synchronized(list) {
while (list.size() == 0) {
list.wait();
}
return list.remove(0);
}
}
public void put(T object) {
synchronized(list) {
list.add(object);
list.notifyAll();
}
}
}
这里封装了一个ArrayList,只能通过同步方法访问或修改(所有同步方法都使用同一个锁,就是ArrayList),所以它是线程安全的。ArrayList 的方法本身不同步并不重要。
如果您可以保证仅在一个同步块内访问集合,或者对集合的每次访问都被同一对象上的同步块包围,那么您应该是安全的,但这通常是一个很难证明的假设,并且很容易被可能追随您的其他开发人员破坏。
仅当访问集合的所有代码都已同步并且它们使用相同的“对象”来同步它时。
例如,下面的代码不会同步,因为它们同步到不同的对象。
public class Foo {
private final Collection<object> collection;
public void Foo(Collection<object> collection) {
this.collection = collection;
}
public synchronized add(Object o) {
this.collection.add(o);
}
}
public class Bar {
private final Collection<object> collection;
public void Bar(Collection<object> collection) {
this.collection = collection;
}
public synchronized print() {
for (Object o : collection) { System.out.println(o); }
}
}
然后您可能会遇到一种情况,您希望Object
打印一个 o,因为您认为它是之前添加的,但是正在执行此操作的线程在添加完成之前就停止了。
如果你有一个标志表明你可以访问某个地方,那么更容易想象这一点。如果旗子高了,你就不能进入区块。当您创建一个 Class 实例并绑定到它时,始终会创建此人。因此,在下面的代码中,我们将有三个“旗人”。
...
Collection<Object> objs = new ArrayList<Object>();
Foo foo = new Foo(objs);
Bar bar = new Bar(objs);
...
该synchronized
声明指示旗人在有人通过它后升起它的旗帜,并在它存在该块时将其放下。因为我们将同步设置为类方法,所以它绑定到该实例的“标志人”。因此,不同的“旗人”会向进入处理收集的街区的人举手,但由于他们彼此不同步,所以即使对方升旗,他们也会让任何人进入。
要解决这个问题,您只需要一个人来处理标志。为此,您需要一个共享旗人。在这种情况下,您可以使用集合本身。所以,你会有类似的东西
public class Foo {
private final Collection<object> collection;
public void Foo(Collection<object> collection) {
this.collection = collection;
}
public synchronized add(Object o) {
synchronized (collection) {
this.collection.add(o);
}
}
}
public class Bar {
private final Collection<object> collection;
public void Bar(Collection<object> collection) {
this.collection = collection;
}
public print() {
synchronized (collection) {
for (Object o : collection) { System.out.println(o); }
}
}
}
因为只有收藏“flag person”在升旗,所以每个人都会根据“谁先来”而不是“谁先完成”来访问收藏。
我想我的解释比它应该的要困难一些,但我希望它可以有所帮助。如果我可以在这里画,它可能会更好理解:P