Pull to refresh

Comments 93

Не думаю, что стоит брать этот экскурс, как хороший источник информации. Использованный в нем пример реализации Singleton на java во-первых не работает, а во-вторых использует имена переменных типа "test1".
Я не думаю, не смотря на то, что статья Java-ориентированная, она все же больше относиться к "Совершенному коду", на мой взгляд.
А у вас нет желания опубликовать серию статей по Java? Это было бы очень полезно.
Это была пробная статья. Если будет достаточно читателей, то я напишу еще несколько статей относящихся к Java программированию.
UFO just landed and posted this here
Видимо все же напишу=) И дабы не оставлять в вас сомнения, эту статью я написал сам. Используя некоторые источники, разумеется.
Кстати, добавте в статью те случаи, когда Singelton является антипаттерном. Например, когда в него засовывают "глобалбные" переменные.
В данной статье я специально опустил эти детали и сконцентрировался только на реализации singleton функционала. Но я с вами согласен, есть множество неправильных использований синглтона, которые только вредят дизайну.
Как раз знание этих вещей поможет писать совершенный код. Вам был бы огромный респект и плюс в карму (хотя я уже плюсанул :-)), если бы вы изучили эти случаи и дополнили ими статью.
Этих случаев достаточно много, и они, скорей всего, относятся к несовершенному дизайну, а не к несовершенному коду. Возьмем ваш пример - "..когда в него засовывают "глобалбные" переменные..", с точки зрения кода, все может работать отлично, но с точки зрения дизайна - ряд проблем на лицо.
Ну тогда ждем отдельную статью по дизайну :-)
не согласен. синглтон прекрасно может хранить глобальные переменные.

Наглядный пример: паттерн Registry
Статья интересная безусловно. Одно замечание - я бы вместо термина "аппликация" использовал "приложение". Режет глаза термин.
Действительно режет=) Исправил, спасибо.
огромное спасибо за статью.был бы вам очень признателен,если бы вы писали еще что нибудь про Java:)
Когда делали скриншоты с Эклипса, то 2, 3 и 4я картинки получили размазаны.
Нет, у меня все картинки в хорошем качестве. Видимо хабр, подгоняя картинки под какие-то свои размеры, слегка растягивает их, тем самым вызывает эффект смазывания.
Почему в первом примере для многопоточной среды нельзя сделать вызов getInstance() синхронизованым (synchronized) ? Это решило бы сразу все проблемы.
Именно это я и сделал в 4-ом варианте решения.
Ну у вас какой-то обходной путь. Как только вы поняли, что оно не подходит для multi-thread, вы начали не пойми что придумывать, когда надо только добавить "synhronized"
Да. Спорить наверное не нужно, ибо DOKA сделал неплохое замечание. Я также когда читал, споткнулся на этом месте. Ну раз не многопоточный, то стало быть и желательно одним предложением написать почему он такой и добавить, что логично, разумеется, воткнуть synchronized и закрыть вопрос. Сославшись на то, что такой случай будет рассмотрен ниже и у него есть свой нюанс.

Второй момент который чуть резанул глаз, это фраза - Реализация Singleton в JAVA. Я не знаю прав я или нет с точки зрения моего интуитивного ощущения русского языка, но когда я вижу фразу ..В Java… я ожидаю, что это УЖЕ реализовано внутри самой платформы и человек просто описывает как там это сделано. Если же речь идёт о том как самому реализовать что-то средствами языка, я ожидаю увидеть оборот ...НА Java.. То есть - как мне реализовать это на языке Java.

Что же касается статьи в целом, то наверное это одна из самых толковых технических статей на Хабре за долгий период. Обычно люди, когда вставляют куски кода в статью, вырывают их из контекста и этот контекст не описывают. То есть пишут в духе ... я вот вчера ночью, лихорадочно кодируя (препарируя вирус...)... открыл для себя, что тут можно было вставить вот такой крутой оператор... а посему на утро хочу поделиться этим с хабра-людьми. Это всё хорошо, но... Что кодировал? Куда вставил? Зачем вставил и кому это нужно?
У вас действительно чётко и внятно описан ходовой и нужный паттерн, и приведены основные варианты его реализации с их плюсами и минусами. Статья логически закончена и реально полезна разработчику. Просто как букварь. Лан, не буду тут раcшаркиваться :-), но что хотел сказать, то сказал.
А performance?
Если getInstance() вызывается в циклах постоянно?
Если надо вызывать из цикла, то можно использовать вар. №5. В остальных случаях в угоду читаемости следует использовать sinhronized на метод. И что-то мне подсказывает, что результат в действительности будет не сильно различаться.
UFO just landed and posted this here
Зависит от цикла. Если простой for то действительно не правильно, а если идет логический цикл процесса с кучей логики, то никуда от этого не денешься.
UFO just landed and posted this here
UFO just landed and posted this here
Ещё бы кто-нибудь на Хабре написал beginners tutorial по Hibernate.. Было бы клёво :)
Будет время - напишу. Чем вас не устраивает туториал, который находится на оффсайте Hibernate? Или этот, например, если вам нужно на русском языке http://www.javaportal.ru/java/articles/h….
Могу предположить, что а) русский вариант просто не был найден (я вот пару лет назад пытался - не получилось) или б) в случае публикации на хабре можно будет уточнить у автора неясные моменты.
Нет. В C++ совершенно другая семантика работы с объектами.
В Scala ("the new java") это реализовано на уровне синтаксиса языка - можно объявлять сразу object, а не class
Это не совсем то же самое — хочется ленивой инстанциации
это порочное желение.
Если хочется ленивой загрузки, значит у объекта есть жизненный цикл (например, взаимодействие с каким-нубдь ресурсом или большие требования памяти / cpu). А это уже по определению не Singleton
расскажите это заказчику, который хочет чтобы окошко приложение отрисовывалось через 2 секунды после запуска и его не волнует что правильно, а что нет. Вместе с тем некоторые синглтоны вполне могут инициализироваться от чего-нибудь медленного, да хотя бы от чтение с диска.
это не проблема, просто не надо этого делать в конструкторе
И в каком тогда месте вы собираитесь инициализировать синглтон?
это для вас проблема?! в любом. можно явно методом init(), можно неявно при обращении к любому методу, паттерны кэширования никто не отменял.
круто, спасибо! :)
большую часть информации я и так знал, но некоторые моменты были познавательными.

IJ Idea, кстати, автоматом по дефолту генерит второй вариант
было бы здорово если бы в подобном очень дружелюбном изложении
на хабре написал бы кто-нибудь пару статей и про другие основные шаблоны
а конкретно с примерами их использования в своих проектах

книжки книжками, но там всегда одни и те же везде примеры - как будто все только и делают, что
клепают интернет магазины и студентов в базу заносят
Полностью согласен. Паттерны тоже неким образом относятся к совершенному коду. Было бы инетесрено узнать об особенностях их применения на практике, т.к. в моей компании нет людей, способных понять простейшие принципы разработки программного обспечения в команде.
Отличная статья! Автору спасибо! Побольше бы таких :)
с нетерпением буду ждать продолжение и о яве, и по тематике блога.
автору — спасибо!
Будет нелишне добавить
public Object clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException();
}
я думаю, что все-же лишне, тк метод clone() является protcted в Object. И раз уж всеравно, в основном, все Синглтоны будут final, то перегружать метод clone() не имеет смысла.
Статья интересная, плюсанул, но. Но нужно помнить об осторожности и технике безопасности. Когда даете детям в руки такую игрушку, очень важно упомянуть, как НЕ НАДО ей играться, а то так сдуру и тесты сломать можно.
Синглтон выглядит очень привлекательно, а на самом деле во многих популярных случаях это все-таки антипаттерн.
Синглетон полезный и нужный паттерн для чётко очерченных случаев, тщательно описанных в его описании. Называть его антипаттерном, это как называть кухонный нож вредным инструментом на том простом основании, что им можно кого-то зарезать. Ну да, наверное можно, но если бы он сопровождался инструкцией, там было бы внятно написано, что им нужно картошку чистить. И только.

В противном случае большинство окружающих нас вещей можно было бы легко зачислить в антипаттерны. Авто, качели, табуретки... Антипаттерн – это неправильное использование нужной и полезной вещи. Из этого абсолютно никак не следует, что мы не должны знать и шарахаться правильного применения нужных и полезных вещей. :-)
enum Singleton {
INSTANCE;
public static Singleton getInstance() { return INSTANCE; }
}
минусующему предлагаю почитать мнение Блоха, который, как ни странно, тоже считает, что a single-element enum type is the best way to implement a singleton.
Действительно, очень интересный вариант. Испаравил социальную несправедливость, где только мог. Спасибо.
Честно говоря, я не могу доказать, что в данном случае гарантирована безопасность в многопоточной среде, но я на 95% уверен, что авторы спецификации Java5 это предусмотрели. Надо будет порыться в спецификации или дизассемблировать какой-нибудь пример.
примерчик дизассемблировал, все нормально там с многопоточностью, по структуре реализация enum это развитие второго варианта, со всеми наворотами enum'ов, которые, цитируя Блоха, добавляют лаконичность синтаксического сахара, сериализацию "из коробки", и серьезную безопасность, в т.ч. от разных изощренных атак.
Если честно, то я так и думал. Вот только сериализация в синглтонах обычно только проблемы приносит=) Ну и, наверно, я редко сталкивался с атаками на мои синглтоны..хотя, всегда лучше перебдеть, чем недобдеть=)
в
enum Singleton {
INSTANCE;
public static Singleton getInstance() { return INSTANCE; }
}


public static Singleton getInstance() { return INSTANCE; }
строчка лишняя имхо.

Если уж enum , то Singleton.INSTANCE
не совсем так, пользователь не обязан знать, что singleton реализован как enum, он просто пользуется им как обычным классом-синглтоном вызывая getInstance(), который может даже интерфейс возвращать.
Пара замечаний по 5 пункту.
1. volatile вообще-то считается самым быстрым способом синхронизации в Java. Быстрее бывает только полное отстуствие синхронизации. Более того: в режиме чтения, который используется в примере, скорость доступа к volatile переменной практически не отличается от скорости доступа к несинхронизированной переменной. Товарищ Allen Holub был здесь неправ, или его неправильно поняли.
2. Корректная работа данного когда под Java версии < 1.5 тоже не гарантируется :)
Подробнее можно почитать у Билла Пью
http://www.ibm.com/developerworks/ru/library/j-jtp03304/index.html
Здесь очень неплохо написано про volatile, final и синхронизацию, советую прочитать
Хотелось бы добавить пару основных вариантов использования синглтона. В некоторых случаях Singleton используется для экономии ресурсов, а в некоторых для реального контроля количества инстансов объекта в системе. В первом случае система проглотит неумелую реализацию без особых проблем, например если синглтон, являющийся фабрикой для создания объектов другого типа, создастся в каждом класслоадере, скорее всего это не будет критической проблемой, кроме конечно проблемы "чистого" решения с точки зрения оптимизации. Если речь идет о контроле объектов, например, конфигурационные параметры системы, которые могут динамически обновляться, то тут конечно грамотная реализация очень важна. Плюс если приложение кластерное, к вариантам реализации синглтона добавляются такие как, per cluster basis или per node basis, что опять же зависит от требований к классу. Вообщем есть о чем подумать на досуге, если вдруг обнаружилось, что класс можно реализовать синглтоном.
Вот ещё хорошая статья (да и вообще сайт хороший) на эту тему :)
чёрт, вот ссылка: http://skipy.ru/technics/singleton.html
Отличная статья! Но есть ещё один способ реализации синглетона - вызовы можно обрабатывать в одной нитке, скажем в Event Dispath Thread. Если запрос к синглетону идет из другой нити, его можно обработать, скажем, используя SwingUtilities.invkoeAndWait(...).

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

P.S. давно хотел написать пару статень по java. плюс пару статей по сертификации у Sun, если кому интересно, но пока к сожалению не могу.
P.S.S. интересный 5-ый вариант реализации, как-то не пользовался таким, обычно просто использовал синхранизацию на get методе, возьму на вооружение
Однопоточность обсуждается в одном варианте и одномн абзаце. Куда уж короче.
PS использовать синхронизацию на get() не имееет никакого смысла. Пора менять концепцию=)

PSS но, всеравно спасибо за позитивно-конструктивный комментарий
помойму вы сами себе противоречите, почему же не имеет смысла использовать сихнхронизацию на get ? Если у вас в 4А-ом примере используется сихронизация именно на get методе. Хотя конечно реализация 4Б-ым вариантом намного логичней. Реализация 1,2,3 фактически одно и тоже, и расматривает однопоточные решения. Различаются только способом создания объекта, и возможности обработки исключения на этапе инициализации объета или конструктора.
варианты 2,3 работают в многопоточной среде и не используют синхронизацию
Да ладно )) я вам могу как минимум привести 2-3 примера где вариант 2,3 не будет работать в многопоточной среде, и скорее всего выволется по exception'у. К примеру один поток запускает этот класс для инициализации, и тут вдруг срабатывает FullGC, все объекты и потоки переходят опять в состояние Runnable, и после этого JVM выбирает потом которые первый пойдет на выполнения, и нету гарантии что первый потом пойдет именно тот который был до FullGC. Дальше, если инициализация объекта достаточно затратное время -допустим коннект к базе данных, или еще что-нибудь. То я вас уверяю что без сихнронизации один поток скорее всего получит null в ответ, или выволиться StaticInitializationException. Причем еще такая реализация без сихнронизации может сильно отличаться в разных средах допустим на Solaris или Windows
я не понял вашего примера, но могу смело вам сказать, что вы не правы. В вариантах 2,3 объект инициализируется класслоадером, поэтому null никто не получит.

И уж точно непричем здесь Solaris и Windows=) Я согласен, что есть некороые различия в виртуальных машинах, но не на таком уровне.
просто я разрабатываю системы реального времени со сверх большой нагрузкой, и каких только "не может быть" у нас не было, и каких разниц только в реализациях и поведения Solaris, Linux не было.
А системы отличаются очень сильно. К примеру Windows закрывает сокеты сразу же, а solaris вообще не закрывает сокеты, а переводит их в состояние TIME_WAIT(и таких примеров куча).
По поводу примеру, какой смысл надеятся на правильнную реализацию static class loader при многопоточном режиме ? я почти уверен что можно сэмулировать StaticInitializationException при подходве 2-3 в многопоточном обращении к объекту.
Если уж вы разарабатываете "системы реального времени со сверх большой нагрузкой", то должны занть цену синхронизации.
Цена синхронизации конечно важна, но намного важней чтоб не нарушалась целостность данных при многопоточной работе.
Мне не очень понравился пятый пример, все не мог понять чем. Нашел на javaworld.com, плюс на ibm.com, и я нашел объяснения - того что мне не нравится.
1) то что в 5-ом примере используете двойную проверку - это хорошо, если бы ее не было внутри сихнронизованной секции, это бы вообще работало не корректно и не был даже синглтон.
2) чем плох все-таки 5-ый пример в отличии от синхронизации именно метода getInstance(), вот что пишет ibm:
The theory behind double-checked locking is perfect. Unfortunately, reality is entirely different. The problem with double-checked locking is that there is no guarantee it will work on single or multi-processor machines.

The issue of the failure of double-checked locking is not due to implementation bugs in JVMs but to the current Java platform memory model. The memory model allows what is known as "out-of-order writes" and is a prime reason why this idiom fails.
Как раз что и я и говорил - при работе на реальных JVM в исключительных ситуациях.
К примеру вот такой вариант будет работать всегда:
public static Singleton getInstance()
{
if (instance == null)
{
synchronized(Singleton.class) { //1
Singleton inst = instance; //2
if (inst == null)
{
synchronized(Singleton.class) { //3
//inst = new Singleton(); //4
instance = new Singleton();
}
//instance = inst; //5
}
}
}
return instance;
}
Большая часть инфы и пример взят от сюда:
http://www.ibm.com/developerworks/library/j-dcl.html
Так же эту ситауацию еще расматривают тут
http://www.javaworld.com/jw-01-2001/jw-0112-singleton.html?page=6
Думаю на это дискуссия закончилась ) я не мог точно объяснить почему это плохо, теперь объяснение есть, поэтому я немного не в ту степь пошел при возникшем споре.
Чтож, очень обидно, что вы начинаете спор не прочитав статью внимательно. Как и ваше утверждение на тему многопоточности 2 и 3 варианов. Так и здесь, я написал, что Double-checked locking в своем оригинальном варианте не работает, но начиная с Java 5, починили модификатор volatile, и если его использовать, то Double-checked locking заработает. Жаль, что вы читаете по диагонали. Спор с вами мне стал не интересен, если хотите задавать вопросы - пожалуйста, но спорить с вами более я не буду.
2,3 вполне будут работать - classloader синхронизирует инициализацию класса. Иначе корректная инициализация классов в multithreaded программе была бы в принципе невозможна.
UFO just landed and posted this here
подозреваю, что если в последнем варианте synchronized блок вынести в отдельную процедуру, то все сработает.
неплохо. спасибо. ждем остальных паттернов. было бы интересно почитать и обсудить с народом потом.
Вот очень интересная статья на тему signleton:
www.javenue.info/post/83

Основная идея в том, что диспетчеризация вызова метода работает гораздо быстрее чем условный оператор (меньше тиков).
Говоря по простому: на реальном параллельном выполнении мы получаем огромную экономию процессорного времени.
Уверен, статья будет интересна абсолютно всем настоящим Java-программистам.
UFO just landed and posted this here
UFO just landed and posted this here
нет, не должно. будет работать также.
UFO just landed and posted this here
что-то я тебя не особенно понимаю. Напиши, пожалуйста, код и покажи где не будет блокировки.
UFO just landed and posted this here
Если instance!=null — то до synchronized итак не дойдет юудет сразу return instance
UFO just landed and posted this here
Sign up to leave a comment.

Articles