Блокировка используется либо для видимости , либо для защиты некоторых данных от одновременной модификации , которая может привести к гонке.
Когда вам нужно просто сделать примитивные операции типа атомарными, доступны такие опции, как AtomicInteger
и тому подобное.
Но предположим, что у вас есть два целых числа, которые связаны друг с другом, такие как x
и y
координаты, которые связаны друг с другом и должны быть изменены атомарным образом. Тогда вы защитите их, используя тот же замок.
Замок должен защищать только состояние, связанное друг с другом. Не меньше и не больше. Если вы используете synchronized(this)
в каждом методе, то даже если состояние класса не связано, все потоки будут сталкиваться с конфликтом, даже если обновляется несвязанное состояние.
class Point{
private int x;
private int y;
public Point(int x, int y){
this.x = x;
this.y = y;
}
//mutating methods should be guarded by same lock
public synchronized void changeCoordinates(int x, int y){
this.x = x;
this.y = y;
}
}
В приведенном выше примере у меня есть только один метод, который мутирует x
и y
, и не два разных метода, так как x
и y
связаны между собой, и если бы я дал два разных метода для мутации x
и y
отдельно, тогда это не было бы потокобезопасным.
Этот пример просто для демонстрации и не обязательно, как это должно быть реализовано. Лучший способ сделать это - сделать это НЕЗАВИСИМЫЙ .
Теперь в противоположность примеру Point
, есть пример TwoCounters
, уже предоставленный @Andreas, где состояние, которое защищается двумя различными блокировками, поскольку состояние не связано друг с другом.
Процесс использования различных блокировок для защиты несвязанных состояний называется Блокировка чередования или Блокировка разделения