Java synchronization and collections

丶灬走出姿态 提交于 2019-12-11 09:29:12

问题


If a synchronized block of code contains an unsynchronized collection. Is the collection considered thread safe? If not, can you provide any practical scenarios where two threads could unsafely access the collection within the synced code?

Thanks.


回答1:


Only if ALL the code that access the collection is synchronized and they use the same "object" to synchronize it.

For example, the code below would not be synchronized because they are synced to different objects.

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

Then you could have a situation that you expected a Object o to be printed because you thought that it was added before, but the thread that was doing so was halted before the add was finished.

It's easier to imagine this like if you have someone that has a flag to indicate that you can access some place. If the flag is high up, you can not enter the block. This person is always created when you create an Class instance and it's bound to it. So, in the code bellow we would have three "flag person".

...
Collection<Object> objs = new ArrayList<Object>();
Foo foo = new Foo(objs);
Bar bar = new Bar(objs);
...

the synchronized statement indicates the flag person to raise its flag after someone passes through it and put it down when it exists the block. Because we set the synchronized to the class method, it's bound to the "flag person" of that instance. So, diffent "flag persons" would raise their hands to someone enter the block where the collection is handled, but because they both are not synchronized to each other, they would let anyone enter, even if the other has its flags raised.

To solve this, you need only one person to handle the flags. To do this, you need a shared flag person. In this case, you could use the collection itself. So, you would have something like

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

Because only collection "flag person" is raising its flag, everyone will access the collection accordingly to "who comes first" and not "who finishes first".

I think I made my explanation a little more difficult than it should hehe but I hope it can helps. If I could draw here, it would probably be better understood :P




回答2:


Here's a really simple object, an unbounded queue, that I used in another example here:

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

There is an ArrayList encapsulated here, it can only be accessed or modified through a synchronized method (and all the synchronized methods use the same lock, that being the ArrayList), so it is threadsafe. It doesn't matter that the ArrayList's methods are not themselves synchronized.




回答3:


If you can guarantee that the collection is only accessed inside that one synchronized block, or that every access to the collection is surrounded by a synchronized block on the same object, then you should be safe, but that is often a very difficult assumption to prove, and can easily be broken by other developers that may come after you.



来源:https://stackoverflow.com/questions/14571143/java-synchronization-and-collections

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!