Может ли этот синглэнд Java повторно перестраиваться в WebSphere 6?

Я пытаюсь выявить проблему в нашей системе, и следующий код меня беспокоит. В нашем методе doPost() в основном сервлете происходит следующее: имена изменены для защиты виновных:

...
if(Single.getInstance().firstTime()){
   doPreperations();
}
normalResponse();
...

Singleton 'Single' выглядит так:

private static Single theInstance = new Single();

private Single() {
...load properties...
}

public static Single getInstance() {
    return theInstance;
}

С помощью способа установки статического инициализатора вместо проверки нулевого значения параметра InInstance в методе getInstance() можно ли перестраивать его снова и снова?

PS - Мы запускаем WebSphere 6 с приложением на Java 1.4

Ответы

Ответ 1

Я нашел это на сайте Sun:

Несколько синглтонов, одновременно загружаемых погрузчиками разного класса

Когда два загрузчика классов загружают класс, у вас на самом деле есть две копии класса, и каждый может иметь свои собственные Экземпляр Singleton. То есть особенно в сервлетах работа в определенных сервлет-двигателях (например, iPlanet), где каждый Сервлет по умолчанию использует свой собственный класс погрузчик. Два разных сервлета доступ к совместному Синглтону, в факт, получить два разных объекта.

Несколько загрузчиков классов происходят больше обычно, чем вы думаете. когда браузеры загружают классы из сети для использования апплетами они используют отдельный загрузчик классов для каждого сервера адрес. Аналогично, Jini и RMI системы могут использовать отдельный класс загрузчик для разных кодовых баз из которого они загружают файлы классов. Если ваша собственная система использует пользовательский класс погрузчики, все те же проблемы могут возникают.

Если загружаются загрузчики разных классов, два класса с тем же именем, даже одно и то же имя пакета, рассматриваются как четкие - даже если они фактически byte-for-byte того же класса. различные загрузчики классов представляют различные пространства имен, которые различают классов (даже если классы "имена одинаковы), так что два MySingleton классы на самом деле различны. (См." Кластерные погрузчики как Механизм пространства имен "в разделе Ресурсы.) Поскольку два объекта Singleton принадлежат двух классов с тем же именем, это будет появляются на первый взгляд, что есть два объекта Singleton одного и того же класс.

Цитирование.

В дополнение к вышеуказанной проблеме, если firstTime() не синхронизирован, вы также можете иметь проблемы с потоками.

Ответ 2

Нет, он не будет строиться снова и снова. Он статический, поэтому он будет создан только один раз, сразу, когда класс будет затронут в первый раз Classloader.

Только исключение - если у вас есть несколько загрузчиков классов.

(из GeekAndPoke):

alt text

Ответ 3

Как уже упоминалось, статический инициализатор запускается только один раз для загрузчика классов.

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

Звучит как неприятный набор зависимостей.

Ответ 4

Нет никакой разницы между использованием статического инициализатора и ленивой инициализацией. На самом деле гораздо проще испортить ленивую инициализацию, которая также обеспечивает синхронизацию. JVM гарантирует, что статический инициализатор всегда запускается до доступа к классу, и это произойдет один раз и только один раз.

Это говорит о том, что JVM не гарантирует, что ваш класс будет загружен только один раз. Однако даже если он загружен более одного раза, ваше веб-приложение по-прежнему будет видеть только соответствующий синглтон, так как он будет загружен либо в загрузчик классов веб-приложений, либо в его родительский. Если у вас развернуто несколько веб-приложений, firstTime() будет вызываться один раз для каждого приложения.

Наиболее очевидные вещи, которые нужно проверить, это то, что firstTime() необходимо синхронизировать и чтобы перед тем, как выйти из этого метода, установлен флаг firstTime.

Ответ 5

Нет, он не будет создавать несколько копий "Single". (Проблема загрузчика классов будет посещаться позже)

Реализация, которую вы описали, описывается как "Желаемая инициализация" в книге Briant Goetz - Java Concurrency на практике.

public class Single
{
    private static Single theInstance = new Single();

    private Single() 
    { 
        // load properties
    }

    public static Single getInstance() 
    {
        return theInstance;
    }
}

Однако код вам не нужен. Ваш код пытается выполнить ленивую инициализацию после создания экземпляра. Это требует, чтобы вся клиентская библиотека выполняла "firstTime()/doPreparation()" перед ее использованием. Вы будете полагаться на клиента, чтобы делать правильные вещи, которые делают код очень хрупким.

Вы можете изменить код следующим образом, чтобы не было дублирующего кода.

public class Single
{
    private static Single theInstance = new Single();

    private Single() 
    { 
        // load properties
    }

    public static Single getInstance() 
    {   
        // check for initialization of theInstance
        if ( theInstance.firstTime() )
           theInstance.doPreparation();

        return theInstance;
    }
}

К сожалению, это плохая реализация ленивой инициализации, и это не будет работать в параллельной среде (например, в контейнере J2EE).

Существует много статей, посвященных инициализации Singleton, в частности, по модели памяти. JSR 133 рассмотрел многие недостатки в модели памяти Java в Java 1.5 и 1.6.

С Java 1.5 и 1.6 у вас есть несколько вариантов, и они упоминаются в книге " Эффективная Java от Джошуа Блоха.

  • Ожидающее начало, как и выше [EJ Item 3]
  • Lazy Initalization Holder Class Idiom [EJ Item 71]
  • Тип перечисления [EJ Item 3]
  • Двойное проверенное блокирование с "изменчивым" статическим полем [EJ Item 71]

Решение 3 и 4 будет работать только на Java 1.5 и выше. Поэтому лучшим решением будет # 2.

Вот psuedo-реализация.

public class Single
{
    private static class SingleHolder
    {
        public static Single theInstance = new Single();
    }

    private Single() 
    { 
        // load properties
        doPreparation();
    }

    public static Single getInstance() 
    {
        return SingleHolder.theInstance;
    }
}

Обратите внимание, что 'doPreparation()' находится внутри конструктора, поэтому вы гарантируете получение правильно инициализированного экземпляра. Кроме того, вы загружаете JVM lazy class load и не нуждаетесь в синхронизации "getInstance()".

Одна вещь, вы заметили, что статическое поле theInstance не "final" . В примере на Java Concurrency нет "final" , но EJ делает. Возможно, Джеймс может добавить больше цветов к своему ответу на "classloader" и требовании "final" , чтобы гарантировать правильность,

Сказав это, есть побочный эффект, который с использованием "статического финала". Компилятор Java очень агрессивен, когда видит "статический финал" и пытается максимально объединить его. Это упоминается в блоге, размещенном Джереми Мэнсоном.

Вот простой пример.

файл: A.java

public class A
{
    final static String word = "Hello World";
}

файл: B.java

public class B
{
    public static void main(String[] args) {
        System.out.println(A.word);
    }
}

После компиляции как A.java, так и B.java вы меняете A.java следующим образом.

файл: A.java

public class A
{
    final static String word = "Goodbye World";
}

Вы перекомпилируете "A.java" и перезапустите B.class. Результат, который вы получите, -

Hello World

Что касается проблемы загрузчика классов, то ответ да, вы можете иметь более одного экземпляра Singleton в нескольких загрузчиках классов. Вы можете найти более подробную информацию о wikipedia. Существует также специальная статья о Websphere.

Ответ 6

Единственное, что я изменил бы в том, что реализация Singleton (кроме использования Singleton вообще) заключается в том, чтобы сделать поле экземпляра окончательным. Статическое поле будет инициализировано один раз, при загрузке класса. Поскольку классы загружаются лениво, вы эффективно получаете ленивую версию для создания.

Конечно, если он загружается из отдельных загрузчиков классов, вы получаете несколько "синглетонов", но это ограничение каждой одиночной идиомы в Java.

EDIT: биты firstTime() и doPreparations() выглядят подозрительными. Не могут ли они быть перемещены в конструктор экземпляра singleton?

Ответ 7

Нет - статическая инициализация instance будет выполняться только один раз. Две вещи, которые следует учитывать:

  • Это не поточно-безопасный (экземпляр не "опубликован" в основной памяти)
  • Ваш метод firstTime, вероятно, вызывается несколько раз, если только не синхронизирован

Ответ 8

В теории он будет построен только один раз. Однако этот шаблон разбивается на разных серверах приложений, где вы можете получить несколько экземпляров "singleton" классов (поскольку они не являются потокобезопасными).

Кроме того, шаблон singleton был сильно критичен. См. Например Синглтон Я люблю тебя, но ты меня подводишь

Ответ 9

Это будет загружаться только один раз, когда класс загружается загрузчиком классов. Этот пример обеспечивает лучшую реализацию Singleton, но он как можно более лёгкий и потокобезопасный. Более того, он работает во всех известных версиях Java. Это решение является наиболее переносимым для разных компиляторов Java и виртуальных машин.


public class Single {

private static class SingleHolder {
   private static final Single INSTANCE = new Single();
}

private Single() {
...load properties...
}

public static Single getInstance() {
    return SingleHolder.INSTANCE;
}

}

Внутренний класс ссылается не ранее (и, следовательно, загружается не ранее загрузчиком класса), чем тот момент, когда вызывается getInstance(). Таким образом, это решение является потокобезопасным, не требуя специальных языковых конструкций (т.е. Изменчивых и/или синхронизированных).

Ответ 10

Не обязательно, чтобы единственный экземпляр был окончательным (это вообще не очень хорошая идея, потому что это позволит вам переключить его поведение с помощью других шаблонов).

В приведенном ниже коде вы можете увидеть, как он создается экземпляр только один раз (при первом вызове конструктора)

дата пакета;

import java.util.Date;

публичный класс USDateFactory реализует DateFactory {   private static USDateFactory usdatefactory = null;

private USDateFactory () { }

public static USDateFactory getUsdatefactory() {
    if(usdatefactory==null) {
        usdatefactory = new USDateFactory();
    }
    return usdatefactory;
}

public String getTextDate (Date date) {
    return null;
}

public NumericalDate getNumericalDate (Date date) {
    return null;
}

}