Если у нас есть 2 класса, которые работают на одном и том же объекте под разными потоками, и мы хотим избежать условий гонки, нам придется использовать синхронизированные блоки с тем же монитором, что и в следующем примере:
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();
}
}
}
Объект, над которым мы будем работать, будет установлен путем вызова setObject()
, после чего он не изменится. Мы будем использовать объект в качестве монитора. Тем не менее, intelliJ будет предупреждать о синхронизации в поле, отличном от конечного.
В этом конкретном сценарии, является ли нелокальное поле приемлемым решением?
Еще одна проблема с вышеприведенным подходом заключается в том, что не гарантируется, что монитор (mData) будет отмечен по потоку 1 или потоку 2 после того, как он задан потоком 3, потому что " до того, как между установкой и чтением монитора не было установлено взаимосвязи. Его можно было бы наблюдать как null
по потоку 1, например. Является ли мое предположение правильным?
Что касается возможных решений, создание поточной защиты DataObject
не является вариантом. Установка монитора в конструкторе классов и объявление его final
может работать.
ИЗМЕНИТЬ Семантически, необходимое взаимное исключение связано с DataObject
. Именно по этой причине я не хочу иметь дополнительный монитор. Одним из решений было бы добавить методы lock()
и unlock()
на DataObject
, которые необходимо вызвать перед началом работы над ним. Внутри они будут использовать объект Lock
. Таким образом, метод operateOnData()
становится следующим:
void operateOnData() {
mData.lock()
mData.doSomething();
.....
mData.doSomethingElse();
mData.unlock();
}