EDIT: Я предполагаю, что вы имели в виду Queue<Object> objectList
вместо ConcurrentLinkedQueue<Object> objectList
. ConcurrentLinkedQueue<Object>
уже выполняет всю вашу безопасность нитей для вас, то есть вы можете позвонить по номеру objectList.peek()
, чтобы вы не беспокоились о состоянии гонки. Это здорово, если вы разрабатываете многопоточные программы, но не настолько хороши для изучения безопасности потоков.
Ваши методы не обязательно должны быть synchronized
, если у вас есть один поток, работающий на одном экземпляре объекта за раз, но если вам нужно иметь несколько экземпляров класса, все из которых относятся к одной и той же переменной статического класса, вы нужно synchronized
над переменным классом так:
public static void getHeadObject() {
synchronized(safe.objectList) {
System.out.println(objectList.peek().toString());
}
}
Это блокирует objectList
и не позволяет ему быть считан или записан в любом другом потоке, как программа находится внутри блока синхронизации. Сделайте то же самое для всех других методов: synchronized
.
Примечание:
Однако, так как вы делаете только один простой операции GET List.peek()
, вы действительно не нужно синхронизировать по objectList
, так как в состоянии гонки, он будет получать либо одно значение List
или другой. Проблема с условиями гонки заключается в том, что выполняются несколько сложных операций чтения/записи с изменением значения между ними.
Например, если у вас есть класс PairInt
с PairInt.x
и PairInt.y
полей, с тем ограничением, что x = 2y
, и вы хотели бы сделать
System.out.println(myIntPair.x.toString() + ", " + myIntPair.y.toString());
и другой поток обновлял значение x
и y
в в то же время,
myIntPair.y = y + 3;
myIntPair.x = 2 * y;
И запись нити модифицированного myIntPair
между вашим чтения потока myIntPair.x.toString()
и myIntPair.y.toString()
вы можете получить вывод, который выглядит как (10, 8)
, что означает, что если вы работаете при условии, что x == 2 * y
может привести к краху вашей программы.
В этом случае ваше чтение необходимо использовать synchronized
, но и для более простых вещей, как peek()
на простом object
, который добавляется или удаляется, не изменяется, а в очереди, то synchronized
может, в большинстве случаев быть опущен. Фактически, для string
, int
, bool
и т.п. условие для простого чтения следует отбросить.
Однако записи должны всегда быть synchronized
для операций, которые явно не являются потокобезопасными, то есть уже обрабатываются java. И как только вы приобретаете более одного ресурс, или требовать, чтобы ваш ресурс остается неизменным в течение всей операции, как вы делаете несколько строк логики к нему, то вы ДОЛЖНЫ ИСПОЛЬЗОВАТЬsynchronized
Почему 'addObject' метод экземпляра? Почему бы не статично? Почему вы все равно синхронизируете вокруг параллельного объекта? Они уже потокобезопасны. – Wug
Что вы подразумеваете под непоследовательным состоянием? –
Я предполагаю, что он имел в виду 'Queue