13
December
2005

Паттерн Singleton (Одиночка)

Posted in: Паттерны проектирования |

English version of this article you can find here.
Этим постом я открываю цикл статей, посвященных паттернам проектирования. Все написанное мной основывается исключительно на личном опыте.
Паттерны проектирования - это описание некоторых проблем, возникающих во время объектно-ориентированного проектирования, а также способов их ре?ения (как практических, так и теоретических). ?ными словами - это примеры правильных подходов к ре?ению типичных задач проектирования.
Одним из самых распространенных паттернов является Singleton (Одиночка). Задача этого паттерна ограничить количество экземпляров некоторого класса. Зачем это может понадобиться? Об этом читайте ниже. Реализовать же паттерн Singleton на языке Java можно следующим образом:

public final class Singleton {
    private static Singleton _instance = null;

    private Singleton() {}

    public static synchronized Singleton getInstance() {
        if (_instance == null)
            _instance = new Singleton();
        return _instance;
    }
}

Теперь приведу некоторые объяснения по поводу реализации.
Конструктор класса необходимо объявить с модификатором видимости private. Это предотвратит создание экземпляров класса как с помощью класса Singleton, так и с помощью его наследников. В связи с этим к объявлению класса смело можно дописать модификатор final.

Метод getInstance() создаст ровно один экземпляр класса Singleton. Этот метод объявлен как synchronized. Сделано это вот почему. В многопоточных программах при одновременном вызове метода getInstance() из нескольких потоков можно создать несколько экземпляров класса Singleton. А должен остаться только один!

От модификатора synchronized можно избавиться. Для этого _instance нужно проинициализировать:

private static final Singleton _instance = new Singleton(),

а в методе getInstance() убрать конструкцию “if”.

Но использование поздней инициализации (lazy initialization) предпочтительнее в случае, если создание экземпляра класса занимает много времени.

Вот собственно и все…

Ах, да! Зачем нам это нужно.
Мне приходится чаще всего использовать этот паттерн при работе с конфигурацией. ?ногда конфигурацию программы удобно хранить в файле. Допустим, это будет простой текстовый файл “props.txt” со строками типа “ключ=значение”. Нам нужно гарантировать, что конфигурация в программе будет в единственном экземпляре. Вторую мы бы и так не создали, но нужно запретить это делать пользователю класса. ?так,

import java.util.*;
import java.io.*;

public class Configuration {
    private static Configuration _instance = null;

    private Properties props = null;

    private Configuration() {
         props = new Properties();
    	try {
	    FileInputStream fis = new FileInputStream(
                    new File(“props.txt”));
	    props.load(fis);
    	}
    	catch (Exception e) {
    	    // обработайте о?ибку чтения конфигурации
    	}
    }

    public synchronized static Configuration getInstance() {
        if (_instance == null)
            _instance = new Configuration();
        return _instance;
    }

    // получить значение свойства по имени
    public synchronized String getProperty(String key) {
        String value = null;
        if (props.containsKey(key))
            value = (String) props.get(key);
        else {
            // сообщите о том, что свойство не найдено
        }
        return value;
    }
}

Теперь для работы с конфигурацией можно использовать конструкцию вида:

String propValue = Configuration.getInstance()
    .getProperty(propKey).

Если имена свойств в “props.txt” меняться не будут, можно описать их в классе таким образом:

public static final String PROP_KEY = “propKey”,

а значения получать так:

String propValue = Configuration.getInstance()
    .getProperty(Configuration.PROP_KEY).

Паттерн Singleton полезен не только при работе с конфигурациями. Его можно использовать также при написании ConnectionPool, Factory и других вещей.
Вот теперь точно все.

Related: Сколько нужно потоков для обработки пользовательских запросов или крайности при использовании Singleton

41 Comments »

RSS feed for comments on this post. TrackBack URI



Tatiana
February 26, 2006 #

Очень полезная страничка!! Спасибо!!

Igor
August 5, 2006 #

Спасибо. Написано доступно и интересно.

Oleg
November 30, 2006 #

Не плохо было бы убрать synchronized со всей функции, т.к. не очень optimized :)

Vile
December 12, 2006 #

Уже не первый раз Гугль кидает меня с моими вопросами именно сюда). Спасибо автору сайта.

Kat
February 6, 2007 #

а в какой момент выполняются все инициализации, которые до конструктора находятся? при загрузке класса? а что такое загрузка класса?

gdwz
April 10, 2007 #

А всетаки, зачем это нужно? В примере мы имеем объект, содержащий поле props. Почему бы не сделать props статическим полем и выдавать его статическим методом (в котором если надо производить инициализацию props)?

чувак
April 23, 2007 #

очень доходчивый пример, буквально таки на пальцах! Спасибо

Entry_N3
October 7, 2007 #

В книге GoF написано, что они этот паттерн в 90х холостяком называли (solitaire). :)

January 24, 2008 #

2Oleg: если ты имее?ь в виду double checked synchronization, то это не работает и заслуживает отдельной статьи.
Если же имеется в виду использование synchronized блока, то это наоборот менее эффективно, чем синхронизированный метод.

February 14, 2008 #

Хоро?ий пример, я рань?е встречал какой-то невменяемый, а логика этого понятна.

radkat
February 29, 2008 #

Поясните, пожалуйста, почему не требуется synchronized на getProperty ?

Vlad
April 26, 2008 #

А какая необходимость в:
private static Singleton _instance = null; ?

В смысле, почему бы не делать ВСЕГДА :
private static Singleton _instance = new Singleton(); ?

andrew
June 1, 2008 #

2 Vlad
потому что, создание обьекта должно быть в методе getInstance()для того, чтобы создавался тока один обьект, ведь этот паттерн для этого и предназначен :)

Pavel
August 3, 2008 #

а почему бы не сделать:

private static final INSTANCE = new Singelton();

private Singelton(){…}

public static void getInstance(){
return INSTANCE;
}
принципиально то же самое что в примере, но немного изящнее и понятнее, и позволяет отказаться от проверки на null.

Vitaliy
August 4, 2008 #

статья может и понятная. но человеку недавно начав?ему изучать, вряд ли по зубам…..

Fevzi
August 29, 2008 #

Супер! В нете много статей и книг по паттернам, все они так зпутанно объясняют, а вот здесь кратко , с кодом да ещё,очень понятно и пример выбран понятный!
Автору респект!

mm
September 26, 2008 #

Автору космический THANKS!

November 7, 2008 #

Спасиба автору.Понимат легко.ищё жду такие стате

Olka
November 28, 2008 #

To Pavel, To Vlad:
Чтобы не выполнять логику конструктора рань?е времени - то есть рань?е первого обращения. А вдруг в конструкторе надо зачитать и обработать длинющий файл?…

Dmitry
January 8, 2009 #

To Olka
Всё равно статическое поле инициализируется только при загрузке класса, а класс загружается только при первом к нему обращении. Т.к. обращения к классу Configuration делаются только при необходимости прочитать конфигурацию, то вариант со статическими методами работал бы так же, как Singleton. Так что это не тот случай, когда без Синглтона нельзя обойтись (например, когда нужно наследоваться). Но, всё-таки его использование здесь - это хоро?ий стиль.

Vad
February 1, 2009 #

Как-то некрасиво получается…а нельзя сделать один класс Singleton, а потом просто наследоваться от него, а не писать реализацию синглтона в каждом классе, использующем эту идиому снова и снова чтобы не дублировать код? Точно знаю, что в C++ так делается (правда, не без использования ?аблона класса), что выглядит поизящнее…только, вот, насчёт Jav’ы не уверен.

sdv
February 26, 2009 #

Автор о?ибся и это очевидно. При боль?ой нагрузке (старт сотен потоков плюс долгая инициализация) вылезут проблемы.

Верное, классическое, ре?ение - вот:

private volatile static Singleton instance;

public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null)
instance = new Singleton();
}
}
return instance;
}

?ли private static final instance = new Singelton(); и даль?е по тексту.

Отличия между этими ре?ениями очевидны, но, боюсь, автор его не совсем осознает.

Как итог, я бы не стал доверять этому циклу статей. ? вообще, ?МХО стоит читать нормальную документацию.

Не в обиду автору, мы все такими были.

sdv
February 26, 2009 #

Дабы на меня не обижались, дескать при?ел, надул щеки и у?ел, вот первая-же попав?аяся на глаза ссылка, в которой изложена суть вопроса

habrahabr.ru/blogs/complete_code/27108/

В общем, ?МХО, рано автору писать статьи

February 27, 2009 #

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

sdv
March 2, 2009 #

Простите, не обратил внимание на дату статьи.

Да, думаю между Вами в 2005-м и сегодня?ним специалистом огромная разница.

Но все-же, пересмотрите статьи, которые Вы писали ранее, люди их смотрят.

March 2, 2009 #

Спасибо за Ва? отзыв. Постараюсь найти время для приведение в порядок ресурса.

March 22, 2009 #

Вот ссылка по теме, может кого-то заинтересует - http://www.javenue.info/post/83

jenik
July 3, 2009 #

знание патернов - унылое говно. Если хоро?о знае?ь среду и язык, так без знания патерна логично напи?е?ь аналогию или копию за пару секунд.

July 6, 2009 #

jenik: никто не говорит, что без паттернов нельзя прожить. Статьи по паттернам - это такое же обучение, как, например, изучение синтаксиса языка или возможностей среды программирования.

Паттерны проектирования более высокоуровневые и не такие формализованные, как синтаксис языка, поэтому часто они вызывают споры у программистов. Но лично я не считаю, что это ненужное занятие, так как пноимание причин их создания позволяют по-другому взглянуть на ООП.

lativ
August 21, 2009 #

2 sdv
В 2009 можно было бы и предложить вариант через AtomicReference:

public class Singleton {
private static final AtomicReference SINGL_REF = new AtomicReference();

private Singleton() {
}

public static final Singleton getInstance() {
if (SINGL_REF.get() == null) {
SINGL_REF.compareAndSet(null, new Singleton());
}
return SINGL_REF.get();
}
}

Arthur
September 19, 2009 #

Да, статьи очень хоро?ие.
Никто не может быть безупречен…
СПС
Люди их читают…мы в ответе за тех, кого приручили…Автор - учтите это:)

Сергей
October 4, 2009 #

2 sdv
Зачем нужно
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null)
instance = new Singleton();
}
}
return instance;
}
Почему нельзя сделать
public static synchronized Singleton getInstance() {
if (instance == null)
instance = new Singleton();
}
return instance;}???

Shender
October 19, 2009 #

спс на пальцах прям осталось применить

Алекс(Чёрный)
November 1, 2009 #

Костя, синглтон - это красивый способ обойти запрет на глобальные переменные. Кроме того в паттернах хватает других боков наравне с этим. Не мучай себя и других этим. ?мхо авторы паттернов никогда не проводили должного исследования на предмет ре?ения постулиуемых проблемм. Для этого нужно поднять около 100 проектов и чтоб 80 из них или около того дали устойчивый статистический результат. А кто это делал? Неужто не было нужды никогда выбрасывать написаные тобой паттерны и писать новые после изменения требования или ещё чего такого рода?

November 17, 2009 #

2 Алекс(Чёрный):

>> Неужто не было нужды никогда выбрасывать написаные тобой паттерны и писать новые после изменения требования или ещё чего такого рода?

Да постоянно так происходит. Ну и потом я не пи?у паттернами. Просто стараюсь писать нормальный код и рефакторить, если что-то не нравится.
Потом что-то вырисовывается и это “что-то” иногда подпадает под описание какого-то паттерна.

wr120
August 27, 2010 #

спасибо за статью. читал много статей, только на данной понял что и зачем

Volodymyr
October 11, 2010 #

to sdv
>> Как итог, я бы не стал доверять этому циклу статей. вообще, ИМХО стоит читать нормальную документацию.
а сам то читал, со своим общим примером?
http://java.sun.com/developer/technicalArticles/Programming/singletons/

sdv
November 15, 2010 #

2 lativ (August 21, 2009)

Спасибо, недавно эту тему копнул как раз.

2 Сергей (October 4, 2009)

Многопоточность-же. Два потока могут войти в “защищенную зону”.

2 Volodymyr (October 11, 2010)

И что? Потрудитесь дать более развернутый коментарий.

Если речь об этом “Double-checked locking is another common solution but, unfortunately, it does not work”, то там есть отличная ссылка, которая приведет к volative. Что и сделано уже в коде, (приведенном год назад).
Этот пример немного быстрее, чем synchronize getInstance(). В обычной жизни не проявляется, а вот когда много параллельных потоков будет его дергать - может проявится.

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

2 const

Могу я попросить Вас отписаться мне на e-mail? Вопрос есть.

Vito_Corleone
December 22, 2010 #

А чем плоха такая запись?

public static synchronized Singleton getInstance() {
return _instance == null ? new Singleton() : _instance;
}

junior
March 12, 2011 #

Чудес не бывает: Соблюдайте Жава Нотэйшен……….

junior
March 12, 2011 #

да кстати по поводу защещенной зоны…………….

Leave a comment

XHTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <code> <em> <i> <strike> <strong> <pre>