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.
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