synchronized — ключевое слово, позволяющее выполнять потокобезопасный доступ к критическим участкам кода. Оно может использоваться либо для метода (например, public synchronized void foo()), либо для блока кода (synchronized(obj) { ... }). Когда поток входит в synchronized-блок, он получает монитор (lock) объекта. Пока монитор занят, другие потоки не могут войти в другой synchronized-блок, использующий тот же объект в качестве монитора.
Особенности:
this, по статическому объекту (например, по классу), по произвольному объекту.public class Counter { private int count; private final Object lock = new Object(); // приватный объект public void increment() { synchronized(lock) { count++; } } }
Почему лучше использовать приватный lock? Потому что если синхронизироваться по публичному объекту (например, по this или по общедоступной строке), внешний код может также захватить этот монитор, приведя к дедлокам или некорректной работе.
Вопрос: Что произойдет, если синхронизироваться на объекте типа String, содержащем фиксированное значение?
Ответ: Строки в Java интернированы (одни и те же объекты для одинаковых литералов). Если синхронизироваться на строке вида synchronized("lock"), можно случайно пересечься с другим кодом, который синхронизируется на таком же литерале, что приведет к неожиданной блокировке между абсолютно разными частями программы.
synchronized("LOCK") { ... }
История
В многопоточной торговой системе для синхронизации использовали публичные объекты, и внешний код смог захватить lock, что привело к временным дедлокам между потоками разных модулей и простоям на бирже.
История
Молодой разработчик синхронизировал доступ к коллекции по строковому литералу. Другая часть кода тоже синхронизировалась по строке с тем же значением. Эти потоки встали в очередь друг за другом, что вызвало резкое замедление бизнес-логики.
История
Для синхронизации был выбран каждый раз новый объект:
synchronized(new Object()) { ... }. В результате синхронизация не работала вообще, and к данным имели одновременный доступ разные потоки. Это было обнаружено только на нагрузочном тестировании.