Назад к вопросам
Middle
111
questionbank

Какие существуют базовые способы синхронизации в Java?

Sobes Copilot

Получайте ответы в реальном времени

Ответ от нейросети

sobes.tech AI

Основные базовые способы синхронизации в Java:

  1. Ключевое слово synchronized:

    • Используется для синхронизации методов или блоков кода.
    • Метод, помеченный synchronized, блокирует доступ к экземпляру объекта для других синхронизированных методов этого же объекта.
    • synchronized (this): синхронизация по текущему экземпляру объекта.
    • synchronized (ClassName.class): синхронизация по классу, блокирует доступ для всех потоков, работающих с этим классом.
    • synchronized (object): синхронизация по конкретному объекту-монитору.
    class Counter {
        private int count = 0;
    
        // Синхронизированный метод
        public synchronized void increment() {
            count++;
        }
    
        public void decrement() {
            // Синхронизированный блок
            synchronized (this) {
                count--;
            }
        }
    }
    
  2. Ключевое слово volatile:

    • Гарантирует видимость изменений переменной между потоками.
    • Не гарантирует атомарность сложных операций (например, i++), только чтение и запись.
    • Применяется к полям класса.
    class SharedData {
        volatile boolean flag = false;
    
        public void setFlag(boolean value) {
            flag = value; // Изменение флага становится видимым для других потоков
        }
    
        public boolean getFlag() {
            return flag;
        }
    }
    
  3. Явные блокировки (Explicit Locks):

    • Предоставляют более гибкий контроль по сравнению с synchronized.
    • Интерфейс Lock и его реализации, например ReentrantLock.
    • Позволяют выбирать между честным и нечестным режимом блокировки, получать состояние блокировки, прерывать поток во время ожидания блокировки.
    • Требуют явного вызова lock() и unlock(). Важно использовать блок finally для вызова unlock().
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    class Resource {
        private final Lock lock = new ReentrantLock();
    
        public void performAction() {
            lock.lock(); // Захват блокировки
            try {
                // Критическая секция
                System.out.println(Thread.currentThread().getName() + " acquired lock");
                Thread.sleep(100); // Имитация работы
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            } finally {
                lock.unlock(); // Освобождение блокировки
                System.out.println(Thread.currentThread().getName() + " released lock");
            }
        }
    }
    
  4. wait(), notify(), notifyAll():

    • Методы класса Object, используемые для взаимодействия потоков, ожидающих освобождения монитора.
    • wait(): освобождает монитор и переводит поток в состояние ожидания до вызова notify() или notifyAll().
    • notify(): будит один поток, ожидающий на этом мониторе.
    • notifyAll(): будит все потоки, ожидающие на этом мониторе.
    • Должны вызываться внутри синхронизированного блока или метода для того же объекта-монитора.
    class ProducerConsumer {
        private final Object monitor = new Object();
        private boolean isProduced = false;
    
        public void produce() throws InterruptedException {
            synchronized (monitor) {
                while (isProduced) {
                    monitor.wait(); // Ждем, пока потребитель не съест
                }
                // Производим
                System.out.println("Produced");
                isProduced = true;
                monitor.notifyAll(); // Уведомляем потребителей
            }
        }
    
        public void consume() throws InterruptedException {
            synchronized (monitor) {
                while (!isProduced) {
                    monitor.wait(); // Ждем, пока производитель не произведет
                }
                // Потребляем
                System.out.println("Consumed");
                isProduced = false;
                monitor.notifyAll(); // Уведомляем производителей
            }
        }
    }
    

Эти механизмы являются основой для создания безопасного многопоточного кода в Java. Выбор конкретного способа зависит от специфики задачи и требуемого уровня контроля над синхронизацией.