Различные способы записи singleton в Java
классика написания синглтона на java выглядит так:
public class SingletonObject
{
private SingletonObject()
{
}
public static SingletonObject getSingletonObject()
{
if (ref == null)
// it's ok, we can call this constructor
ref = new SingletonObject();
return ref;
}
private static SingletonObject ref;
}
и мы можем добавить синхронизированное ключевое слово, если нам нужно запустить его в многопоточных случаях.
но я предпочитаю писать так:
public class SingletonObject
{
private SingletonObject()
{
// no code req'd
}
public static SingletonObject getSingletonObject()
{
return ref;
}
private static SingletonObject ref = new SingletonObject();
}
что я думаю, более лаконично, но странно, что я не видел никакого образца кода, написанного таким образом, есть ли какие-либо плохие эффекты, если я написал свой код таким образом?
10 ответов
разница между вашим кодом и" образцом кода "заключается в том, что ваш синглтон создается при загрузке класса, в то время как в" образце " версии он не создается до тех пор, пока он не понадобится.
во второй форме, ваш синглтон готовностью loaded, и это на самом деле предпочтительная форма (и первая не является потокобезопасной, как вы сами упомянули). Нетерпеливая загрузка не плохая вещь для производственного кода, но есть контексты, в которых вы можете лениво загружать свои синглеты, как обсуждалось автором Guice, Бобом ли, в Ленивая Загрузка Синглетов что я цитирую ниже:
во-первых, почему вы хотите ленивый нагрузка одноэлементный? В производстве, вы как правило, хотите с нетерпением загрузить все ваши синглеты, так что вы ловите ошибки ранний и принять любое выступление хит вверх фронт, но в тестах и в развитие, вы только хотите нагрузить что вам абсолютно необходимо, чтобы не терять время.
перед Java 1.5 я лениво загружен синглеты с использованием plain old синхронизация, простая, но эффективная:
static Singleton instance; public static synchronized Singleton getInstance() { if (instance == null) instance == new Singleton(); return instance; }
изменения в модели памяти в 1.5 включен пресловутый Дважды Проверено Блокировка (DCL) идиома. Для реализации РСН, вы проверяете
volatile
поле в общий путь и синхронизировать только тогда, когда необходимо:static volatile Singleton instance; public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) instance == new Singleton(); } } return instance; }
но
volatile
разве это не намного быстрее чемsynchronized
,synchronized
is довольно быстро в наше время, и DCL требует больше кода, поэтому даже после 1.5 вышел, Я продолжал использовать plain old синхронизация.представьте мое удивление сегодня, когда Джереми Мэнсон указал мне на инициализация по требованию Держатель (IODH) фразеологизм который требует очень мало кода и ноль накладные расходы на синхронизацию. Ноль, как в даже быстрее, чем
volatile
. IODH требуется такое же количество строк код как простая старая синхронизация, и это быстрее, чем DCL!IODH использует ленивый класс инициализация. В JVM не выполнить статический инициализатор класса, пока вы потрогай что-нибудь в классе. Это относится к статическим вложенным классам, тоже. В следующем например, JLS гарантирует JVM не будет инициализировать
instance
пока кто-то звонкиgetInstance()
:static class SingletonHolder { static Singleton instance = new Singleton(); } public static Singleton getInstance() { return SingletonHolder.instance; }
[...]
обновление: должное, Эффективная Java (авторское право 2001) вся эта картина по пункту 48. Далее он указывает, что вам все еще нужно использовать синхронизацию или DCL в нестатических контекстах.
Я также переключил обработку singleton в мой рамки от синхронизации до DCL и увидел еще 10% производительности boost (по сравнению с до начала работы использование быстрого отражения cglib). Я только используется один поток в моем микро-бенчмарке, таким образом, повышение параллелизма может быть даже больше, учитывая, что Я заменил тяжело оспариваемый замок с a относительно тонкий volatile поле доступ.
обратите внимание, что Джошуа блох теперь рекомендует (начиная с эффективной Java, 2nd ed) реализовать синглеты с использованием одного элемента enum
как указал Jonik.
Ну, в последнем случае одноэлементный объект создается до того, как он когда-либо понадобится, но в большинстве случаев это, вероятно, не ужасно плохо.
кстати, Джошуа блох рекомендует (в Эффективная Java, 2-е изд, пункт 3) реализация синглетов с использованием одноэлементного перечисления:
public enum SingletonObject {
INSTANCE;
}
Он дает следующее обоснование:
[...] он более лаконичен, обеспечивает сериализацию машинное оборудование бесплатно, и обеспечивает броненосец гарантия против нескольких экземпляр, даже перед лицом сложная сериализация или отражение атак. Пока это подход еще широко не принято, одноэлементный тип перечисления является лучшим способ реализации синглтона.
Я бы сказал, что последний код является более стандартным шаблоном. Ваша первая версия не является потокобезопасной. Способы сделать его потокобезопасным включают синхронизацию при каждом доступе или внимательно использование двойной проверки блокировки (которая безопасна с модели памяти Java 5, Если вы ее правильно понимаете).
обратите внимание, что из-за ленивой инициализации классов ваш последний код все равно создаст объект без необходимости, если вы вызовете статические методы на класс, не желая создавать экземпляр.
есть шаблон, использующий вложенный класс для инициализации, который может сделать это более ленивым, но лично вторая форма почти всегда достаточно хороша для меня сама по себе.
есть более подробная информация об этом в эффективной Java, но у меня нет его со мной, чтобы найти номер элемента, я боюсь.
Я думаю, ваша проблема в том, что вы смешиваете Синглтон и ленивую инициализацию. Синглтон может быть реализован с помощью разные стратегии инициализации:
- инициализация при загрузке класса
- ленивая инициализация, которая использует двойную проверку блокировки
- ленивая инициализация с одиночной проверкой (с возможной повторной инициализацией)
- ленивая инициализация, использующая загрузчик классов (класс держателя идиома)
все эти подходы обсуждаются в эффективный Java 2-й пункт 71: используйте ленивую инициализацию разумно.
различные способы реализации синглтон шаблон в Java выглядит следующим образом
public class SingletonClass {
private SingletonClass= null;
public static SingletonClass getInstance() {
if(SingletonClass != null) {
SingletonClass = new SingletonClass();
}
}
}
это не потокобезопасными. Следующие thread safe
реализация одноэлементный шаблон проектирования
1. Драконовская синхронизация
private static YourObject instance;
public static synchronized YourObject getInstance() {
if (instance == null) {
instance = new YourObject();
}
return instance;
}
2.Двойная проверка синхронизации
private static final Object lock = new Object();
private static volatile YourObject instance;
public static YourObject getInstance() {
YourObject r = instance;
if (r == null) {
synchronized (lock) { // While we were waiting for the lock, another
r = instance; // thread may have instantiated the object.
if (r == null) {
r = new YourObject();
instance = r;
}
}
}
return r;
}
3. Держатель инициализации по требованию идиома
public class Something {
private Something() {}
private static class LazyHolder {
static final Something INSTANCE = new Something();
}
public static Something getInstance() {
return LazyHolder.INSTANCE;
}
}
4. Другой использует enum
public enum Singleton {
SINGLE;
public void myMethod(){
}
}
второй способ решит проблему многопоточности, но всегда будет создавать Синглтон, даже если он вам не нужен. Лучший способ сделать это-создать вложенный класс.
public class singleton
{
private singleton()
{
System.out.println("I'am called only when it's needed");
}
static class Nested
{
Nested() {}
private static final singleton instance = new singleton();
}
public static singleton getInstance()
{
return Nested.instance;
}
public static void main(String [] args)
{
singleton.getInstance();
}
}
лучший способ написать одноэлементный класс приведен ниже .Пожалуйста, попробуйте
public final class SingeltonTest {
/**
* @param args
* @return
*/
private static SingeltonTest instance = null;
private SingeltonTest() {
System.out.println("Rahul Tripathi");
}
public static SingeltonTest getInstance() {
if (instance == null) {
synchronized (SingeltonTest.class) {
if (instance == null)
instance == new SingeltonTest();
}
}
return instance;
}
}
// Lazy loading enabled as well as thread safe
класс Синглтон {
private static class SingletonHolder {
public static Singleton instance = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.instance;
}
}
Я согласен с Аноном, и в случае, когда я всегда хочу создать экземпляр синглтона, я бы использовал
public class SingletonObject
{
public static SingletonObject REF = new SingletonObject();
private SingletonObject()
{
// no code req'd
}
}