Ответ 1
Разница между вашим кодом и "образцом кода" заключается в том, что ваш singleton создается при загрузке класса, а в "образцовой" версии он не создается, пока он не понадобится.
Классик написания singleton в java выглядит так:
public class SingletonObject
{
private SingletonObject()
{
}
public static SingletonObject getSingletonObject()
{
if (ref == null)
// it 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();
}
который я считаю более кратким, но, как ни странно, я не видел никакого примера кода, написанного таким образом, есть ли какие-либо плохие последствия, если я написал свой код таким образом?
Разница между вашим кодом и "образцом кода" заключается в том, что ваш singleton создается при загрузке класса, а в "образцовой" версии он не создается, пока он не понадобится.
Во второй форме ваш синглтон загружен с нетерпением, и на самом деле это предпочтительная форма (и первая из них не является потокобезопасной, поскольку вы упомянули ее сами). Желательная загрузка - это не плохо для производственного кода, но есть контексты, где вам может понадобиться ленивая загрузка ваших синглетов, о чем говорил автор Guice, Боб Ли, в Lazy Loading Singletons, которые я цитирую ниже:
Во-первых, зачем вам ленивый груз синглтон? В производстве вы обычно хотят с нетерпением загружать все ваши синглтоны, чтобы вы ломали ошибки рано и добиться какой-либо производительности спереди, но в тестах и во время разработки, вы только хотите загрузить что вам абсолютно необходимо, чтобы не время отходов.
До Java 1.5 я ленивый загружался синглетоны, использующие простой старый синхронизация, простая, но эффективная:
static Singleton instance; public static synchronized Singleton getInstance() { if (instance == null) instance == new Singleton(); return instance; }
Изменения в модели памяти в 1.5 включил печально известный Double-Checked Блокировка (DCL). Чтобы реализовать 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
довольно быстро в настоящее время, и DCL требует больше кода, поэтому даже после того, как вышел 1.5, Я продолжал использовать простой старый синхронизации.Представьте мое удивление сегодня, когда Джереми Мэнсон указал мне на Инициализация по требованию владельца (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 в нестатических контекстах.
Я также переключил однотоновую обработку в моя структура от синхронизации до DCL и увидел еще 10% производительности (по сравнению с тем, как я начал используя быстрое отображение cglib). только я использовал один поток в моем микро-контроле, поэтому повышение до concurrency может быть еще больше, учитывая, что я заменил сильно заблокированный замок с относительно мелкозернистая летучее поле доступ.
Обратите внимание, что Джошуа Блох теперь рекомендует (начиная с Effective Java, 2nd ed) реализовать синглтоны, используя один элемент enum
, как указано Jonik.
Ну, в последнем случае объект singleton создается до того, как он когда-либо понадобится, но в большинстве случаев это, вероятно, не ужасно плохо.
Кстати, Джошуа Блох рекомендует (в "Эффективной Java", 2-е изд., п. 3), реализуя синглтоны, используя одноэлементное перечисление:
public enum SingletonObject {
INSTANCE;
}
Он дает следующее обоснование:
[...] он более краткий, обеспечивает сериализацию техники и предоставляет железная гарантия от нескольких инстанцирования, даже перед лицом сложная сериализация или отражения. Хотя это подход еще не принят, одноэлементный тип перечисления является лучшим способ реализовать синглтон.
Я бы сказал, что последний код является более стандартным. Ваша первая версия не является потокобезопасной. Способы обеспечения безопасности потока включают в себя синхронизацию при каждом доступе или очень тщательное использование блокировки с двойным проверкой (что безопасно с моделью памяти Java 5, если вы это исправите).
Обратите внимание, что из-за ленивости инициализации классов ваш последний код будет по-прежнему создавать объект без необходимости, если вы вызываете статические методы в классе, не создавая экземпляр.
Там шаблон, вложенный вложенный класс, выполняет инициализацию, которая может сделать эту более ленивую, но лично вторая форма почти всегда делает для меня достаточно хорошо.
В "Эффективной Java" есть более подробная информация об этом, но я не имею его со мной, чтобы найти номер элемента, я боюсь.
Я думаю, ваша проблема в том, что вы смешиваете одиночную и ленивую инициализацию. Один синглтон может быть реализован с помощью различных стратегий инициализации:
Все эти подходы обсуждаются в Эффективная Java 2nd Item 71: разумно используйте ленивую инициализацию.
Различные способы реализации singleton pattern в 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(){
}
}
второй способ разрешит проблему многопоточности, но всегда будет создавать Singleton, даже если вам это не понадобится. Лучший способ сделать это - создать класс Nested.
public class singleton
{
private singleton()
{
System.out.println("I'am called only when it 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
class Singleton {
private static class SingletonHolder {
public static Singleton instance = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.instance;
}
}
Я согласен с Anon, и в случае, когда я всегда хочу создать экземпляр синглтона, я бы использовал
public class SingletonObject
{
public static SingletonObject REF = new SingletonObject();
private SingletonObject()
{
// no code req'd
}
}