Ключевое слово synchronized
Ключевое слово synchronized используется для
управления доступом к общим ресурсам из нескольких
потоков. Когда поток входит в синхронизированный
блок или метод, он захватывает монитор объекта.
Другие потоки, пытающиеся войти в этот же
синхронизированный блок или метод, будут
заблокированы до тех пор, пока первый поток
не освободит монитор. Синхронизация может
применяться к методам (экземплярным и
статическим) или к отдельным блокам кода.
В случае синхронизированного метода монитором
является сам объект (для экземплярных методов)
или объект класса (для статических методов).
В случае синхронизированного блока монитор
указывается явно в скобках после ключевого слова.
Синтаксис
// synchronized method
public synchronized void methodName()
{
// code
}
// synchronized static method
public static synchronized void methodName()
{
// code
}
// synchronized block
synchronized (object)
{
// code
}
Пример
Давайте рассмотрим пример, где два потока увеличивают значение счётчика. Без синхронизации результат может быть непредсказуемым:
public class Main
{
private static int counter = 0;
public static void main(String[] args) throws InterruptedException
{
Thread t1 = new Thread(() -> {
for (int i = 0; i < 1000; i++)
{
counter++;
}
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 1000; i++)
{
counter++;
}
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("Counter: " + counter);
}
}
Результат выполнения кода может быть меньше
2000 из-за состояния гонки:
Counter: 1847
Пример
Теперь используем synchronized для
защиты счётчика. Создадим синхронизированный
метод:
public class Main
{
private static int counter = 0;
public static synchronized void increment()
{
counter++;
}
public static void main(String[] args) throws InterruptedException
{
Thread t1 = new Thread(() -> {
for (int i = 0; i < 1000; i++)
{
increment();
}
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 1000; i++)
{
increment();
}
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("Counter: " + counter);
}
}
Теперь результат всегда будет корректным:
Counter: 2000
Пример
Давайте используем синхронизированный блок вместо синхронизированного метода. Это позволяет синхронизировать только критическую секцию:
public class Main
{
private static int counter = 0;
private static final Object lock = new Object();
public static void main(String[] args) throws InterruptedException
{
Thread t1 = new Thread(() -> {
for (int i = 0; i < 1000; i++)
{
synchronized (lock)
{
counter++;
}
}
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 1000; i++)
{
synchronized (lock)
{
counter++;
}
}
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("Counter: " + counter);
}
}
Результат выполнения кода:
Counter: 2000