Thread-safe Singleton in Java using Double Checked Locking Idiom
Singleton Pattern is one of the famous design patterns from the Gang of Four. Even though nowadays it is considered as an anti-pattern, it has served us well in the past. In the Singleton pattern, a class has just one instance throughout its lifetime and that instance is shared between multiple clients. Singleton class has two responsibilities, first to ensure that only instance of the class gets created and second, to provide a method getInstance() so that everyone can get access to that single instance, I mean, global access.
One of the issues, faced by the Singelton design pattern in the multi-threading program is to ensure that just one instance of the class gets created, even if multiple clients called the getInstance() method same time.
Many programmers solved this problem by making the whole getInstance() method synchronized, which results in poor performance because every time a thread enters a synchronization method, it acquires the lock and while it’s been inside the method, no other thread is allowed to enter, even if they are not creating an instance and just accessing already created instance.
How do you solve this problem? Well, you can use Double-checked locking idiom, which you learn in this article. Btw, if you are curious about why Singleton is considered an anti-pattern, I highly recommend the book Games Programming Patterns by Robert Nystrom, though examples are in C++, it’s one of the most readable books on design patterns, you won’t feel bored or cheated.
Threadsafe Singleton using Double Checked Locking Idiom
Double checked locking idiom solves this problem by allowing you to eat your cake and have it as well, it ensures synchronization is used only when an instance of Singleton is created when the getInstance() method is called first time and all other times, the same instance is returned without any synchronization overhead.
As the name suggests, it’s double-checked, which means it checked two times whether _instnace (the singleton instance) is initialized or not, one without synchronization and the other with synchronization.
This double-check ensures that locking is only used when an instance is null i.e. when the first time someone calls getInstance() , all subsequent calls will see _instnace not null hence they will not enter into the synchronization block.
Here is the code of thread-safe singleton pattern using double-checked locking idiom:
/** * Thread Safe Singleton in Java using Double checked locking. * @author WINDOWS 8 * */ public class Singleton < private static volatile Singleton _instance; /** * Double checked locking code on Singleton * @return Singelton instance */ public static Singleton getInstance() < if (_instance == null) < synchronized (Singleton.class) < if (_instance == null) < _instance = new Singleton(); > > > return _instance; > >
How Double Checked Locking Idiom Works
To illustrate the point, how this idiom prevents from two instances being created when two thread simultaneously calls the getInstance() method, let’s see the theory. Suppose, thread T1 calls getInstance() very the first time and sees that _instance is null then it will go inside synchronization block and that point of time it paused.
Now, thread T2 calls getInstance() and it will also see _instance variable null, but it cannot go inside synchronization block because the lock is held by Thread T1, which is inside the synchronization block. Now, thread T1 wake up and creates a new instance of singleton and come out of synchronized block.
After this when thread T2 goes inside synchronized block, it again checks whether _instance is null and this time check fails because _instnace is no more null. So thread T2 comes out of the synchronized block without creating another instance. Further calls to this method return from the first check only.
By the way, double-checked locking idiom was broken before Java 5. It was possible for a thread to see a half initialized instance which will fail the first null check, resulting in returning a half-initialized Singleton. That’s why it’s absolutely critical to make _instnace a volatile variable. The Java memory model updates and happens-before makes double-checked locking works again.
That’s all about how to create thread-safe Singleton in Java, but this is not the only way to create the thread-safe singleton. You can use Enum as Singleton then Java itself will guarantee that only one instance will be created even in the case of multiple threads trying to access it at the same time.
Alternatively, you can also eagerly initialized the Singleton, in that case, it would be initialized on static initializer block at the time of class loading in a thread-safe manner. If you decide to use double-checked locking idiom to make Singleton creation thread-safe, don’t forget the volatile modifier.
Further Reading
If you want to learn more about safe publication techniques in concurrency and other concurrency gotchas, read Java Concurrency in Practice by Brian Goetz, one of the most respected books on Java concurrency.
And, if you want to read more about design patterns, particularly about Singleton patterns with examples from game development, try Game design patterns by Robert Nystrom, one of the interesting books I read this year.
Правильный Singleton в Java
Уверен, каждый из читателей, знает что такое шаблон проектирования “Singleton”, но не каждый знает как его программировать эффективно и правильно. Данная статья является попыткой агрегирования существующих знаний по этому вопросу.
Кроме того, можно рассматривать статью как продолжение замечательного исследования, публиковавшегося на Хабрахабре ранее.
Неленивый Singleton в Java
Автору известно два способа реализации шаблона с нормальной инициализацией.
1 Static field
+ Простая и прозрачная реализация
+ Потокобезопасность
— Не ленивая инициализация
2 Enum Singleton
По мнению Joshua Bloch’а это лучший способ реализации шаблона [1].
+ Остроумно
+ Сериализация из коробки
+ Потокобезопасность из коробки
+ Возможность использования EnumSet, EnumMap и т.д.
+ Поддержка switch
— Не ленивая инициализация
Ленивый Singleton в Java
На момент написания статьи существует как минимум три корректных реализации шаблона Singleton с ленивой инициализацией на Java.
1 Synchronized Accessor
public class Singleton < private static Singleton instance; public static synchronized Singleton getInstance() < if (instance == null) < instance = new Singleton(); >return instance; > >
+ Ленивая инициализация
— Низкая производительность (критическая секция) в наиболее типичном доступе
2 Double Checked Locking & volatile
public class Singleton < private static volatile Singleton instance; public static Singleton getInstance() < Singleton localInstance = instance; if (localInstance == null) < synchronized (Singleton.class) < localInstance = instance; if (localInstance == null) < instance = localInstance = new Singleton(); >> > return localInstance; > >
+ Ленивая инициализация
+ Высокая производительность
— Поддерживается только с JDK 1.5 [5]
2.1 Почему не работает без volatile?
Проблема идиомы Double Checked Lock заключается в модели памяти Java, точнее в порядке создания объектов. Можно условно представить этот порядок следующими этапами [2, 3]:
Пусть мы создаем нового студента: Student s = new Student(), тогда
1) local_ptr = malloc(sizeof(Student)) // выделение памяти под сам объект;
2) s = local_ptr // инициализация указателя;
3) Student::ctor(s); // конструирование объекта (инициализация полей);
Таким образом, между вторым и третьим этапом возможна ситуация, при которой другой поток может получить и начать использовать (на основании условия, что указатель не нулевой) не полностью сконструированный объект. На самом деле, эта проблема была частично решена в JDK 1.5 [5], однако авторы JSR-133 [5] рекомендуют использовать voloatile для Double Cheсked Lock. Более того, их отношение к подобным вещам легко прослеживается из коментария к спецификации:
There exist a number of common but dubious coding idioms, such as the double-checked locking idiom, that are proposed to allow threads to communicate without synchronization. Almost all such idioms are invalid under the existing semantics, and are expected to remain invalid under the proposed semantics.
Таким образом, хотя проблема и решена, использовать Double Checked Lock без volatile крайне опасно. В некоторых случаях, зависящих от реализации JVM, операционной среды, планировщика и т.д., такой подход может не работать. Однако, серией опытов сопровождаемых просмотром ассемблерного кода, генерированного JIT’ом автору, такой случай вопросизвести не удалось.
Наконец, Double Checked Lock можно использовать без исключений с immutable объектами (String, Integer, Float, и т.д.).
3 On Demand Holder idiom
public class Singleton < public static class SingletonHolder < public static final Singleton HOLDER_INSTANCE = new Singleton(); >public static Singleton getInstance() < return SingletonHolder.HOLDER_INSTANCE; >>
+ Ленивая инициализация
+ Высокая производительность
— Невозможно использовать для не статических полей класса
Performance
Для сравнения производительности выше рассмотренных методов, была использована микро-бенчмарка [6], определяющая количество элементарных операций (инкремент поля) в секунду над Singleton объектом, из двух параллельных потоков.
Измерения производились на двухядерной машине Intel Core 2 Duo T7300 2GHz, 2Gb ram и Java HotSpot(TM) Client VM (build 17.0-b17). За единицу скора считается количество инкрементов (а следовательно и захватов объекта) в секунду * 100 000.
(больше — лучше)
Client | Server | |
---|---|---|
Synchronized accessor | 42,6 | 86,3 |
Double Checked Lock & volatile | 179,8 | 202,4 |
On Demand Holder | 181,6 | 202,7 |
Вывод: если правильно подобрать реализацию шаблона можно получить ускорение (speed up) от 2х до 4х.
Summary
Можно выделить следующие короткие советы по использованию того или иного подхода для реализации шаблона “Одиночка” [1].
1) Использовать нормальную (не ленивую) инициализацию везде где это возможно;
2) Для статических полей использовать On Demand Holder idom;
3) Для простых полей использовать Double Chedked Lock & volatile idom;
4) Во всех остальных случаях использовать Syncronized accessor;
Java Class Library & Singleton
Примечательно, что разработчики Java Class Library выбрали наиболее простой способ реализации шаблона — Syncronized Accessor. C одной стороны — это гарантия совместимости и правильной работы. С другой — это потеря процессорного времени на вход и выход из критической секции при каждом обращении.
Быстрый поиск grep’ом по исходникам дал понять, что таких мест в JCL очень много.
Возможно следующая статья будет “Что будет если в Java Class Library правильно написать все Singleton классы?” 🙂
Double-Checked Locking with Singleton
Repeatedly, code that works in dev breaks down in production. Java performance issues are difficult to track down or predict.
Simply put, Digma provides immediate code feedback. As an IDE plugin, it identifies issues with your code as it is currently running in test and prod.
The feedback is available from the minute you are writing it.
Imagine being alerted to any regression or code smell as you’re running and debugging locally. Also, identifying weak spots that need attending to, based on integration testing results.
Of course, Digma is free for developers.
As always, the writeup is super practical and based on a simple application that can work with documents with a mix of encrypted and unencrypted fields.
We rely on other people’s code in our own work. Every day.
It might be the language you’re writing in, the framework you’re building on, or some esoteric piece of software that does one thing so well you never found the need to implement it yourself.
The problem is, of course, when things fall apart in production — debugging the implementation of a 3rd party library you have no intimate knowledge of is, to say the least, tricky.
Lightrun is a new kind of debugger.
It’s one geared specifically towards real-life production environments. Using Lightrun, you can drill down into running applications, including 3rd party dependencies, with real-time logs, snapshots, and metrics.
Learn more in this quick, 5-minute Lightrun tutorial:
Slow MySQL query performance is all too common. Of course it is. A good way to go is, naturally, a dedicated profiler that actually understands the ins and outs of MySQL.
The Jet Profiler was built for MySQL only, so it can do things like real-time query performance, focus on most used tables or most frequent queries, quickly identify performance issues and basically help you optimize your queries.
Critically, it has very minimal impact on your server’s performance, with most of the profiling work done separately — so it needs no server changes, agents or separate services.
Basically, you install the desktop application, connect to your MySQL server, hit the record button, and you’ll have results within minutes:
DbSchema is a super-flexible database designer, which can take you from designing the DB with your team all the way to safely deploying the schema.
The way it does all of that is by using a design model, a database-independent image of the schema, which can be shared in a team using GIT and compared or deployed on to any database.
And, of course, it can be heavily visual, allowing you to interact with the database using diagrams, visually compose queries, explore the data, generate random data, import data or build HTML5 database reports.