digital-garden/dev/java/synchronized.md
Struchkov Mark be8fd578f3
All checks were successful
continuous-integration/drone/push Build is passing
Обновление
2024-10-09 09:23:45 +03:00

143 lines
8.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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