Синхронизированный блок в Java
Блок synchronized (obj) {... } берет монитор
указанного объекта только на время фигурных скобок.
Так можно защитить одно поле, не блокируя весь метод
целиком.
Часто заводят отдельное поле private final Object lock = new Object();,
чтобы внешний код не мог случайно синхронизироваться
на this и обойти ваши правила.
Приращение только внутри блока:
class Counter {
private final Object gate = new Object();
private int total = 0;
void addOne() {
synchronized (gate) {
total++;
}
}
int read() {
synchronized (gate) {
return total;
}
}
}
Между итерациями можно делать работу без монитора, а блок держать коротким:
public class Main {
public static void main(String[] args) throws InterruptedException {
Counter counter = new Counter();
Runnable work = () -> {
for (int i = 0; i < 500; i++) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return;
}
counter.addOne();
}
};
Thread a = new Thread(work);
Thread b = new Thread(work);
a.start();
b.start();
a.join();
b.join();
System.out.println(counter.read());
}
}
Монитор gate один на все потоки, которые
меняют total. Чтение тоже идет под тем же
замком, чтобы не увидеть частично обновленное
значение.
Поле visits и объект gate.
Три потока после короткой паузы увеличивают
счетчик только в synchronized (gate).
Итог после join равен 3.