Как синхронизировать статический метод в Java

Я придумываю этот вопрос при реализации шаблона "одиночка" в Java. Даже при том, что упомянутым ниже примером не является мой реальный код, еще очень похожий на исходный.

public class ConnectionFactory{
    private static ConnectionFactory instance;

    public static synchronized ConnectionFactory getInstance(){
        if( instance == null ){
            instance = new ConnectionFactory();
        }

        return instance;
    }

    private ConnectionFactory(){
        // private constructor implementation
    }
}

Поскольку я не совсем уверен в поведении статического синхронизированного метода, я добираюсь, некоторое предложение от Google - не имеют (или максимально меньше) нескольких статических синхронизированных методов в том же классе. Я предполагаю при реализации статического синхронизированного метода, блокировка принадлежит Объекту класса, используется так, чтобы несколько статических синхронизированных методов могли ухудшить производительность системы.

Я прав? или JVM использует другой механизм для реализации статического синхронизированного метода? Какова лучшая практика, если я должен реализовать несколько статических синхронизированных методов в классе?

Спасибо всем!

С уважением!

8
задан Nathan Hughes 24 March 2016 в 03:55
поделиться

5 ответов

Лучший подход (который вносит как можно меньше изменений в ваш код) - это сделать так:

public class ConnectionFactory{
    private static ConnectionFactory instance = new ConnectionFactory();

    public static ConnectionFactory getInstance(){
        return instance;
    }

    private ConnectionFactory(){
    }
}

Как видите, сейчас нет реальной необходимости в методе getInstance , поэтому вы можно упростить код до:

public class ConnectionFactory{
    public static final ConnectionFactory INSTANCE = new ConnectionFactory();

    private ConnectionFactory(){
    }
}

UPD о синхронизации: лучший способ - синхронизация на блокировке, которая не видна внешним классам, то есть:

public class ConnectionFactory{
    private static final Object lock = new Object();

    public static void doSmth() {
        synchronized (lock) {

           ...
        }
    }

    public static void doSmthElse() {
        synchronized (lock) {

           ...
        }
    }
}

Есть много дискуссий о том, «почему синхронизация на это это плохая идея »(например, этот ), я думаю, что то же самое актуально и для синхронизации в классе.

7
ответ дан 5 December 2019 в 12:07
поделиться

Есть несколько способов создать синглтон.

Один из рекомендуемых способов - использовать перечисление (гарантированно создать только один экземпляр):

public enum ConnectionFactory {

  INSTANCE;

}

Или вы можете создать его статически при загрузке класса:

public class ConnectionFactory {

  private static ConnectionFactory INSTANCE = new ConnectionFactory();

  private ConnectionFactory() {}

  public static ConnectionFactory getInstance() {
    return INSTANCE;
  }    

}

Если вам нужно лениво загрузить его, вы можете использовать эту идиому ( а не антишаблон блокировки с двойной проверкой )

public class ConnectionFactory {

  private static class ConnectionFactoryHolder {
    private static ConnectionFactory INSTANCE = new ConnectionFactory();
  }

  public static ConnectionFactory getInstance() {
    return ConnectionFactoryHolder.INSTANCE;
  }

}
3
ответ дан 5 December 2019 в 12:07
поделиться

Да, статические методы синхронизируются по своему объекту класса. Я бы не стал беспокоиться о производительности здесь, так как, вероятно, это не будет вашей горячей точкой производительности. Делайте это просто, оптимизируйте, когда и где вам нужно.

2
ответ дан 5 December 2019 в 12:07
поделиться

Статические синхронизированные методы используют блокировку класса. В случае вашего примера это будет доступ к блокировке объекта класса ConnectionFactory. Лучшая практика - не удерживать замки дольше, чем необходимо. Наличие нескольких синхронизированных методов само по себе не является проблемой.

2
ответ дан 5 December 2019 в 12:07
поделиться

Эффективная Java рекомендует использовать перечисления для создания синглтонов. Итак, ваш код будет выглядеть примерно так:

public enum ConnectionFactory{
INSTANCE;

// Other factory methods go here.

}

}

0
ответ дан 5 December 2019 в 12:07
поделиться