If we have 2 classes that operate on the same object under different threads and we want to avoid race conditions, we'll have to use synchronized blocks with the same monitor like in the example below:
class A {
private DataObject mData; // will be used as monitor
// thread 3
public setObject(DataObject object) {
mData = object;
}
// thread 1
void operateOnData() {
synchronized(mData) {
mData.doSomething();
.....
mData.doSomethingElse();
}
}
}
class B {
private DataObject mData; // will be used as monitor
// thread 3
public setObject(DataObject object) {
mData = object;
}
// thread 2
void processData() {
synchronized(mData) {
mData.foo();
....
mData.bar();
}
}
}
The object we'll operate on, will be set by calling setObject()
and it will not change afterwards. We'll use the object as a monitor. However, intelliJ will warn about synchronization on a non-final field.
In this particular scenario, is the non-local field an acceptable solution?
Another problem with the above approach is that it is not guaranteed that the monitor (mData) will be observed by thread 1 or thread 2 after it is set by thread 3, because a "happens-before" relationship hasn't been established between setting and reading the monitor. It could be still observed as null
by thread 1 for example. Is my speculation correct?
Regarding possible solutions, making the DataObject
thread-safe is not an option. Setting the monitor in the constructor of the classes and declaring it final
can work.
EDIT Semantically, the mutual exclusion needed is related to the DataObject
. This is the reason that I don't want to have a secondary monitor. One solution would be to add lock()
and unlock()
methods on DataObject
that need to be called before working on it. Internally they would use a Lock
Object. So, the operateOnData()
method becomes:
void operateOnData() {
mData.lock()
mData.doSomething();
.....
mData.doSomethingElse();
mData.unlock();
}
volatile
you might indeed still run into issues. Try explaining more of the use case, this looks like there is a much better way of doing this with some dedicated concurrency tool. – Incorruptibleprivate
in just one class, you still need synchronization. – During