Java 同步和集合



如果同步的代码块包含未同步的集合。集合是否被视为线程安全?如果没有,您能否提供任何实际方案,其中两个线程可能会不安全地访问同步代码中的集合?

谢谢。

这是一个非常简单的对象,一个无限队列,我在这里的另一个示例中使用了它:

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,因为您认为它是以前添加的,但是这样做的线程在添加完成之前就停止了。

更容易想象这一点,就像你有一个标志来指示你可以访问某个地方一样。如果旗帜高高在上,则无法进入块。此人始终是在创建类实例时创建的,并且此人绑定到该实例。因此,在下面的代码中,我们将有三个"旗人"。

...
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); }
       }
    }
}
因为只有收藏"旗

人"在升旗,所以每个人都会根据"谁先来"而不是"谁先完成"来访问收藏。

我想我让我的解释比它应该的要困难一些,但我希望它能有所帮助。如果我能在这里画画,可能会更好地理解:P

最新更新