РЕПЕТИТОР математика физика информатика
Для школьников и студентов. Подтягивание пробелов. ЦЭ, ЦТ, ОГЭ, ЕГЭ.
Идет набор на ЛЕТО. Жмите для подробностей:)
987 of 1163 menu

Ключевое слово synchronized

Ключевое слово synchronized используется в многопоточном программировании для защиты критических секций кода от одновременного доступа нескольких потоков. Когда один поток входит в синхронизированный блок или метод, он захватывает монитор указанного объекта (или класса), и все остальные потоки, пытающиеся войти в этот же блок, вынуждены ждать, пока монитор не будет освобождён. В качестве монитора для нестатических методов выступает сам экземпляр объекта (this), для статических - объект класса (Main.class), а для синхронизированных блоков - объект, переданный в круглых скобках.

Синтаксис

Синхронизированный метод:

public synchronized void methodName() { // critical section }

Синхронизированный статический метод:

public static synchronized void methodName() { // critical section }

Синхронизированный блок:

synchronized (monitorObject) { // critical section }

Пример

Давайте рассмотрим пример, в котором два потока увеличивают общий счётчик. Без синхронизации результат может быть неверным из-за состояния гонки:

public class Main { private static int counter = 0; public static void main(String[] args) throws InterruptedException { Runnable task = () -> { for (int i = 0; i < 10000; i++) { counter++; } }; Thread t1 = new Thread(task); Thread t2 = new Thread(task); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println(counter); } }

Результат выполнения кода может быть меньше ожидаемого 20000, например:

17352

Пример

Теперь добавим synchronized-блок, чтобы защитить операцию инкремента:

public class Main { private static int counter = 0; private static final Object lock = new Object(); public static void main(String[] args) throws InterruptedException { Runnable task = () -> { for (int i = 0; i < 10000; i++) { synchronized (lock) { counter++; } } }; Thread t1 = new Thread(task); Thread t2 = new Thread(task); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println(counter); } }

Теперь результат выполнения кода всегда корректен:

20000

Пример

Давайте посмотрим, как работает synchronized-метод. Один поток удерживает монитор, а второй ожидает освобождения:

public class Main { public static void main(String[] args) throws InterruptedException { Printer printer = new Printer(); Thread t1 = new Thread(() -> printer.print("Thread 1")); Thread t2 = new Thread(() -> printer.print("Thread 2")); t1.start(); t2.start(); t1.join(); t2.join(); } } class Printer { public synchronized void print(String name) { for (int i = 0; i < 3; i++) { System.out.println(name + ": " + i); try { Thread.sleep(100); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } } }

Результат выполнения кода (потоки не перемешиваются):

Thread 1: 0 Thread 1: 1 Thread 1: 2 Thread 2: 0 Thread 2: 1 Thread 2: 2

Пример

Синхронизированный статический метод блокирует монитор класса, а не экземпляра:

public class Main { public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(() -> Counter.increment()); Thread t2 = new Thread(() -> Counter.increment()); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println(Counter.getCount()); } } class Counter { private static int count = 0; public static synchronized void increment() { for (int i = 0; i < 10000; i++) { count++; } } public static int getCount() { return count; } }

Результат выполнения кода:

20000

Смотрите также

  • ключевое слово volatile,
    которое обеспечивает видимость изменений между потоками
  • класс ReentrantLock,
    который предоставляет более гибкую альтернативу synchronized
  • класс AtomicInteger,
    который выполняет атомарные операции без блокировок
  • метод join,
    который ожидает завершения потока
Мы используем cookie для работы сайта, аналитики и персонализации. Обработка данных происходит согласно Политике конфиденциальности.
принять все настроить отклонить