← Назад к вопросам

На каком объекте происходит синхронизация статического метода с ключевым словом synchronized

2.0 Middle🔥 131 комментариев
#Многопоточность и асинхронность

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Синхронизация статических методов в Java

Ключевой принцип: Синхронизация статического метода происходит на объекте Class, связанном с классом, в котором объявлен метод. Каждый класс в Java имеет единственный объект Class, который представляет этот класс в JVM.

Механизм работы

Когда вы объявляете статический метод с ключевым словом synchronized, весь метод блокируется для доступа из разных потоков. Блокировка происходит на объекте Class, который можно получить через ИмяКласса.class.

public class Counter {
    private static int count = 0;
    
    // Синхронизированный статический метод
    public static synchronized void increment() {
        count++;
        System.out.println("Count: " + count);
    }
    
    // Эквивалентная запись с явным указанием монитора
    public static void decrement() {
        synchronized(Counter.class) {
            count--;
            System.out.println("Count: " + count);
        }
    }
}

Различия между статической и нестатической синхронизацией

АспектСтатический synchronized методНестатический synchronized метод
Объект монитораОбъект Class (напр., Counter.class)Экземпляр класса (this)
Влияние на другие методыБлокирует все статические синхронизированные методы классаБлокирует все нестатические синхронизированные методы того же экземпляра
ДоступДоступен без создания экземпляра классаТребует создания экземпляра класса

Практический пример с проблемой параллелизма

public class BankAccount {
    private static double totalBalance = 0;
    private double personalBalance;
    
    // Статический синхронизированный метод - блокировка на BankAccount.class
    public static synchronized void updateTotalBalance(double amount) {
        totalBalance += amount;
        System.out.println(Thread.currentThread().getName() + 
                          " обновил общий баланс: " + totalBalance);
    }
    
    // Нестатический синхронизированный метод - блокировка на экземпляре (this)
    public synchronized void updatePersonalBalance(double amount) {
        personalBalance += amount;
        System.out.println(Thread.currentThread().getName() + 
                          " обновил личный баланс: " + personalBalance);
    }
}

// Тестовый класс
public class SynchronizationDemo {
    public static void main(String[] args) {
        // Поток 1 работает со статическим методом
        Thread thread1 = new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                BankAccount.updateTotalBalance(100);
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        
        // Поток 2 также работает со статическим методом
        Thread thread2 = new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                BankAccount.updateTotalBalance(50);
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        
        thread1.start();
        thread2.start();
    }
}

Важные особенности и рекомендации

  1. Изоляция блокировок:

    public class DatabaseManager {
        private static final Object staticLock = new Object();
        private final Object instanceLock = new Object();
        
        // Альтернатива synchronized методу - лучше для производительности
        public static void staticOperation() {
            synchronized(staticLock) {
                // Критическая секция для статических данных
            }
        }
        
        public void instanceOperation() {
            synchronized(instanceLock) {
                // Критическая секция для данных экземпляра
            }
        }
    }
    
  2. Взаимное влияние:

    • Статические синхронизированные методы блокируют друг друга, даже если работают с разными данными
    • Нестатические синхронизированные методы разных экземпляров НЕ блокируют друг друга
    • Статические и нестатические синхронизированные методы могут выполняться параллельно
  3. Производительность:

    // Плохо: весь метод блокирован
    public static synchronized void processData() {
        // Длительная операция
    }
    
    // Лучше: минимальная критическая секция
    public static void processDataOptimized() {
        // Несинхронизированная работа
        synchronized(DatabaseManager.class) {
            // Только необходимая критическая секция
        }
        // Продолжение несинхронизированной работы
    }
    
  4. Наследование и интерфейсы:

    • Синхронизация не является частью сигнатуры метода
    • При переопределении synchronized метода, synchronized модификатор не наследуется автоматически
    • Для интерфейсов нельзя объявлять synchronized методы (кроме методов по умолчанию в Java 8+)

Распространённые проблемы и решения

Проблема 1: Взаимная блокировка (deadlock) при использовании нескольких классов

// Риск взаимной блокировки
class ClassA {
    public static synchronized void methodA() {
        ClassB.methodB();
    }
}

class ClassB {
    public static synchronized void methodB() {
        ClassA.methodA();
    }
}

Решение: Использовать упорядоченное получение блокировок или ReentrantLock

Проблема 2: Избыточная синхронизация

// Избыточная синхронизация замедляет выполнение
public class Config {
    private static final Properties props = new Properties();
    
    // Не всегда нужна синхронизация
    public static synchronized String getProperty(String key) {
        return props.getProperty(key);
    }
}

Решение: Использовать потокобезопасные коллекции или ConcurrentHashMap

Альтернативы в современных Java-приложениях

Для Android-разработки особенно важны современные подходы:

  1. Использование ReentrantLock:

    public class SafeCounter {
        private static int count = 0;
        private static final ReentrantLock lock = new ReentrantLock();
        
        public static void increment() {
            lock.lock();
            try {
                count++;
            } finally {
                lock.unlock();
            }
        }
    }
    
  2. Атомарные операции:

    import java.util.concurrent.atomic.AtomicInteger;
    
    public class AtomicCounter {
        private static final AtomicInteger count = new AtomicInteger(0);
        
        public static void increment() {
            count.incrementAndGet();
        }
    }
    
  3. Kotlin-подход (для Android с Kotlin):

    object SingletonManager {
        private var counter = 0
        
        @Synchronized
        fun increment() {
            counter++
        }
        
        // Или с использованием мьютекса
        private val mutex = Mutex()
        
        suspend fun safeIncrement() = mutex.withLock {
            counter++
        }
    }
    

Вывод

Синхронизация статических методов — мощный инструмент для обеспечения потокобезопасности на уровне класса, но требует осторожного использования. Понимание того, что блокировка происходит на объекте Class, помогает избежать распространённых ошибок проектирования и оптимизировать производительность многопоточных приложений на Android.