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

StackOverflow https://stackoverflow.com/questions/225895

  •  03-07-2019
  •  | 
  •  

Вопрос

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

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

Синглтон "Single" выглядит следующим образом:

private static Single theInstance = new Single();

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

public static Single getInstance() {
    return theInstance;
}

С учетом того, что это настроено на использование статического инициализатора вместо проверки на наличие null theInstance в методе getInstance (), может ли это перестраиваться снова и снова?

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

Это было полезно?

Решение

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

  

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

     

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

     

Несколько загрузчиков классов встречаются чаще   обычно, чем вы думаете. когда   браузеры загружают классы из сети   для использования апплетами, они используют   отдельный загрузчик классов для каждого сервера   адрес. Точно так же Джини и РМИ   системы могут использовать отдельный класс   загрузчик для разных кодовых баз   из которого они скачивают файлы классов.   Если ваша собственная система использует пользовательский класс   загрузчики, все те же вопросы могут   возникают.

     

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

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

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

Другие советы

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

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

(из GeekAndPoke ):

alt text

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

Я хотел бы взглянуть на метод firstTime () - почему работа в doPreparations () не может быть обработана в самом синглтоне?

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

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

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

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

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

Реализация, которую вы описали, описана как «Стремительная инициализация» в книге Брайана Гетца - Параллелизм Java на практике .

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

    private Single() 
    { 
        // load properties
    }

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

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

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

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 & amp; 1.6, у вас есть несколько вариантов, и они упоминаются в книге Эффективная Java Джошуа Блох.

<Ол>
  • Стремительная инициализация, как указано выше [EJ Item 3]
  • Идиома класса «Ленивый держатель инициализации» [EJ Item 71]
  • Тип Enum [EJ Item 3]
  • Блокировка с двойной проверкой с помощью 'volatile' статического поля [EJ Item 71]
  • Решения 3 и 4 будут работать только в Java 1.5 и выше. Так что лучшим решением будет №2.

    Вот псевдо-реализация.

    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;
        }
    }
    

    Обратите внимание, что doPreperation () находится внутри конструктора, поэтому вы гарантированно получите правильно инициализированный экземпляр. Кроме того, вы возвращаетесь к отложенной загрузке классов JVM и не нуждаетесь в какой-либо синхронизации getInstance ().

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

    Сказав это, есть побочный эффект, связанный с использованием «static 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 в нескольких загрузчиках классов. Вы можете найти дополнительную информацию в википедии . Существует также специальная статья на Websphere .

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

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

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

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

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

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

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

    Это будет загружено только один раз, когда класс будет загружен загрузчиком классов.Этот пример обеспечивает лучшую реализацию 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().Таким образом, это решение потокобезопасно, не требуя специальных языковых конструкций (т.е.изменчивый и/или синхронизированный).

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

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

    дата упаковки;

    импортируйте java.util.Дата;

    открытый класс USDateFactory реализует DateFactory { частный статический 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;
    }
    

    }

    Лицензировано под: CC-BY-SA с атрибуция
    Не связан с StackOverflow
    scroll top