8.6 KiB
aliases | tags | date | zero-link | parents | linked | |
---|---|---|---|---|---|---|
|
2024-10-09 |
synchronized — это один из ключевых механизмов в Java для управления Многопоточность в Java и обеспечения безопасности при доступе к разделяемым ресурсам. Этот модификатор используется для того, чтобы ограничить доступ к критическим секциям кода, тем самым предотвращая состояния гонки (../other/Race condition).
Как работает synchronized
Synchronized может применяться к методам или блокам кода. Когда поток входит в метод или блок, помеченный synchronized
, он получает Монитор в Java (lock) на объект, связанный с этим кодом. Другие потоки не могут получить доступ к этому коду до тех пор, пока монитор не будет освобожден. Это гарантирует, что только один поток может одновременно выполнять данный код.
В этом примере, synchronized
гарантирует, что методы increment
и getCount
не будут выполняться одновременно несколькими потоками, предотвращая некорректные обновления переменной count
.
public class Counter {
private int count = 0;
public synchronized void increment() {
count++;
}
public synchronized int getCount() {
return count;
}
}
Иногда удобнее синхронизировать не весь метод, а только его часть. В этом случае можно использовать блок synchronized
:
public void increment() {
synchronized (this) {
count++;
}
}
Особенности
- Мониторы привязаны к объектам, а не к методам. Если вы синхронизируете метод экземпляра (instance method), то монитором является текущий объект (this). Если метод статический, то монитором выступает сам класс (ClassName.class).
- Блокировки могут привести к взаимной блокировке (../../../../_inbox/Deadlock), если два потока пытаются захватить друг у друга мониторы в неправильном порядке.
Ограничения
- Синхронизация снижает производительность, так как потоки вынуждены ожидать освобождения блокировки.
- Недостаточная гибкость: при сложных задачах многопоточности использование
synchronized
может стать неэффективным, и тогда лучше рассмотреть более современные и гибкие альтернативы, такие как классы из пакетаjava.util.concurrent
(например,ReentrantLock
).
Частые проблемы и ошибки
Чрезмерная синхронизация (Over-synchronization)
Проблема: Синхронизация всего метода или слишком большого объема кода может снизить производительность программы, так как потоки вынуждены ждать доступа к синхронизированным участкам, даже если это не требуется.
Решение: Следует синхронизировать только ту часть кода, которая действительно требует защиты.
Синхронизация на неправильном объекте
Проблема: Если синхронизация происходит на локальном или временном объекте, это не защитит данные, так как каждый поток будет работать с собственной копией объекта.
public class Counter {
private int count = 0;
public void increment() {
// Неправильная синхронизация на локальном объекте
Object lock = new Object();
synchronized (lock) {
count++;
}
}
}
Здесь каждый поток создает собственный объект lock
, и, следовательно, синхронизация не работает.
Решение: Синхронизацию нужно выполнять на общем объекте (например, this
или другом общем объекте).
Синхронизация на статических и нестатических методах
Проблема: Если не понимать, как работают мониторы для статических и нестатических методов, можно случайно создать ситуацию, когда доступ к объектам будет синхронизирован некорректно.
public class StaticSyncExample {
private static int staticCount = 0;
private int instanceCount = 0;
public static synchronized void incrementStatic() {
staticCount++;
}
public synchronized void incrementInstance() {
instanceCount++;
}
}
Здесь вызов incrementStatic
синхронизируется на классе StaticSyncExample.class
, а incrementInstance
— на конкретном объекте класса. Это может привести к недоразумениям в случаях, когда ожидается один и тот же монитор для всего кода.
Еще один пример
public class Container {
private static final List<String> list = new ArrayList<>();
synchronized void addEntry(String s) {
list.add(s)
}
}
Здесь у нас будет проблема, если будет 2+ объекта класса Container
, так как каждый из них будет синхронизироваться на своем объекте, а переменная list
у нас статическая и ее экземпляр единственный на всю программу
Решение: Нужно учитывать, что статические и нестатические методы используют разные мониторы. Чтобы синхронизировать весь класс (включая как статические, так и нестатические данные), можно использовать общий объект для синхронизации.
Пропуск синхронизации для чтения и записи
Проблема: Разрешение чтения переменных без синхронизации, тогда как их запись синхронизирована, может привести к непредсказуемым результатам, поскольку значения могут быть прочитаны в неконсистентном состоянии.
public class Counter {
private int count = 0;
public synchronized void increment() {
count++;
}
// Неправильное чтение без синхронизации
public int getCount() {
return count;
}
}
В этом случае разные потоки могут получить некорректное значение переменной count.
Мета информация
Область:: ../../meta/zero/00 Java разработка Родитель:: Многопоточность в Java Источник:: Создана:: 2024-10-09 Автор::