Struchkov Mark
be8fd578f3
All checks were successful
continuous-integration/drone/push Build is passing
143 lines
8.6 KiB
Markdown
143 lines
8.6 KiB
Markdown
---
|
||
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) -->
|
||
|