什么是在Java中实现单例模式的有效方法?
什么是在Java中实现单例模式的有效方法?
使用枚举:
public enum Foo { INSTANCE; }
Joshua Bloch在Google I / O 2008上的Effective Java Reloaded演讲中解释了这种方法: 链接到video 。 另请参阅演示文稿的幻灯片30-32( effective_java_reloaded.pdf ):
实现一个可串行单例的正确方法
public enum Elvis { INSTANCE; private final String[] favoriteSongs = { "Hound Dog", "Heartbreak Hotel" }; public void printFavorites() { System.out.println(Arrays.toString(favoriteSongs)); } }
编辑: “有效的Java”的在线部分说:
“这种方法在function上等同于公共领域的方法,除了它更简洁,提供了免费的序列化机制,并且为多重实例化提供了一个铁证,即使面对复杂的序列化或reflection攻击。尚未被广泛采用, 单元枚举types是实现单例的最好方法 。“
根据使用情况,有几个“正确的”答案。
由于java5最好的办法是使用枚举:
public enum Foo { INSTANCE; }
在java5之前,最简单的情况是:
public final class Foo { private static final Foo INSTANCE = new Foo(); private Foo() { if (INSTANCE != null) { throw new IllegalStateException("Already instantiated"); } } public static Foo getInstance() { return INSTANCE; } public Object clone() throws CloneNotSupportedException{ throw new CloneNotSupportedException("Cannot clone instance of this class"); } }
让我们来看看代码。 首先,你希望这个class是最后的。 在这种情况下,我使用了final
关键字来让用户知道它是最终的。 然后,您需要使构造函数为私有,以防止用户创build自己的Foo。 从构造函数中抛出exception可以防止用户使用reflection来创build第二个Foo。 然后你创build一个private static final Foo
字段来保存唯一的实例,而一个public static Foo getInstance()
方法来返回它。 Java规范确保构造函数只在第一次使用类时才被调用。
当你有一个非常大的对象或沉重的构造代码,也有其他可用的静态方法或字段,可能在需要实例之前使用,那么只有那么你需要使用延迟初始化。
您可以使用private static class
来加载实例。 代码将如下所示:
public final class Foo { private static class FooLoader { private static final Foo INSTANCE = new Foo(); } private Foo() { if (FooLoader.INSTANCE != null) { throw new IllegalStateException("Already instantiated"); } } public static Foo getInstance() { return FooLoader.INSTANCE; } }
由于行private static final Foo INSTANCE = new Foo();
只有在FooLoader类被实际使用时才会被执行,这样就保证了懒惰的实例化,并保证是线程安全的。
当你也想要序列化你的对象时,你需要确保反序列化不会创build一个副本。
public final class Foo implements Serializable { private static final long serialVersionUID = 1L; private static class FooLoader { private static final Foo INSTANCE = new Foo(); } private Foo() { if (FooLoader.INSTANCE != null) { throw new IllegalStateException("Already instantiated"); } } public static Foo getInstance() { return FooLoader.INSTANCE; } @SuppressWarnings("unused") private Foo readResolve() { return FooLoader.INSTANCE; } }
方法readResolve()
将确保唯一的实例将被返回,即使在程序的上一次运行中序列化对象时也是如此。
Stu Thompson发布的解决scheme在Java5.0及更高版本中是有效的。 但我宁愿不使用它,因为我认为它是容易出错的。
很容易忘记易变的陈述,很难理解为什么这是必要的。 如果没有volatile,由于双重检查locking反模式,此代码不再是线程安全的。 请参阅Java并发实践的第16.2.4节中的更多内容。 简而言之:这种模式(在Java5.0之前或者没有volatile语句)可能会返回一个对仍然处于不正确状态的Bar对象的引用。
这种模式是为了性能优化而发明的。 但是这实际上不再是一个真正的问题了。 下面的懒惰初始化代码是快速的,更重要的是更容易阅读。
class Bar { private static class BarHolder { public static Bar bar = new Bar(); } public static Bar getBar() { return BarHolder.bar; } }
免责声明:我刚才总结了所有的真棒答案,并写在我的话。
在实现Singleton的时候,我们有2个选项
1.延迟加载
2.早期加载
延迟加载会增加比特开销(很多说实话),所以只有当你有一个非常大的对象或沉重的build设代码,也有其他可用的静态方法或字段,可能需要一个实例之前使用它,然后只有你需要使用懒惰初始化。否则select早期加载是一个不错的select。
实现Singleton最简单的方法是
public class Foo { // It will be our sole hero private static final Foo INSTANCE = new Foo(); private Foo() { if (INSTANCE != null) { // SHOUT throw new IllegalStateException("Already instantiated"); } } public static Foo getInstance() { return INSTANCE; } }
一切都很好,除了早期的单身人士。 让我们试试懒加载单身人士
class Foo { // Our now_null_but_going_to_be sole hero private static Foo INSTANCE = null; private Foo() { if (INSTANCE != null) { // SHOUT throw new IllegalStateException("Already instantiated"); } } public static Foo getInstance() { // Creating only when required. if (INSTANCE == null) { INSTANCE = new Foo(); } return INSTANCE; } }
到目前为止这么好,但我们的英雄将不会生存,而与多个邪恶的线索谁想要许多我们的英雄的例子单独作战。 所以让我们保护它免受邪恶的multithreading
class Foo { private static Foo INSTANCE = null; // TODO Add private shouting constructor public static Foo getInstance() { // No more tension of threads synchronized (Foo.class) { if (INSTANCE == null) { INSTANCE = new Foo(); } } return INSTANCE; } }
但是还不足以保护英雄,真的!!! 这是我们可以/应该做的最好的帮助我们的英雄
class Foo { // Pay attention to volatile private static volatile Foo INSTANCE = null; // TODO Add private shouting constructor public static Foo getInstance() { if (INSTANCE == null) { // Check 1 synchronized (Foo.class) { if (INSTANCE == null) { // Check 2 INSTANCE = new Foo(); } } } return INSTANCE; } }
这被称为“双重locking成语”。 很容易忘记易变的陈述,很难理解为什么这是必要的。
详情请见http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html
现在我们对邪恶的线索是肯定的,但是这个残酷的系列化呢? 我们必须确保即使在去串行化的时候也不会有新的对象被创build
class Foo implements Serializable { private static final long serialVersionUID = 1L; private static volatile Foo INSTANCE = null; // Rest of the things are same as above // No more fear of serialization @SuppressWarnings("unused") private Object readResolve() { return INSTANCE; } }
方法readResolve()
将确保唯一的实例将被返回,即使在我们的程序的前一次运行中序列化对象时也是如此。
最后,我们添加了足够的保护来防止线程和序列化,但是我们的代码看起来笨重和难看。 让我们的英雄弥补
public final class Foo implements Serializable { private static final long serialVersionUID = 1L; // Wrapped in a inner static class so that loaded only when required private static class FooLoader { // And no more fear of threads private static final Foo INSTANCE = new Foo(); } // TODO add private shouting construcor public static Foo getInstance() { return FooLoader.INSTANCE; } // Damn you serialization @SuppressWarnings("unused") private Foo readResolve() { return FooLoader.INSTANCE; } }
是的,这是我们非常相同的英雄:)
由于行private static final Foo INSTANCE = new Foo();
只有当FooLoader
类被实际使用时才会被执行,这需要关注懒惰的实例化,
并保证是线程安全的。
我们到目前为止,这是实现我们所做的一切最好的方式
public enum Foo { INSTANCE; }
内部将被视为像
public class Foo { // It will be our sole hero private static final Foo INSTANCE = new Foo(); }
这就是没有更多的序列化,线程和丑陋的代码的恐惧。 也ENUMS单身人士懒惰地初始化 。
这种方法在function上等同于公共领域的方法,除了它更简洁,提供了免费的序列化机制,并且提供了对多重实例化的铁证,即使面对复杂的序列化或reflection攻击。 虽然这种方法尚未被广泛采用,但是单元枚举types是实现单例的最好方法。
-Joshua Bloch在“Effective Java”
现在你可能已经意识到为什么ENUMS被认为是实现Singleton的最佳方式,并且感谢您的耐心:)
在我的博客上更新了它。
Java 5+中的线程安全:
class Foo { private static volatile Bar bar = null; public static Bar getBar() { if (bar == null) { synchronized(Foo.class) { if (bar == null) bar = new Bar(); } } return bar; } }
编辑 :注意这里的volatile
修饰符。 :)这很重要,因为没有它,JMM(Java内存模型)不保证其他线程看到其值的变化。 同步没有考虑到这一点 – 它只是序列化访问该代码块。
编辑2 :@Bno的答案详细比尔·普(FindBugs)推荐的方法,并争议更好。 去阅读并且回答他的答案。
忘记延迟初始化 ,这太麻烦了。 这是最简单的解决scheme:
public class A { private static final A INSTANCE = new A(); private A() {} public static A getInstance() { return INSTANCE; } }
确保你真的需要它。 做一个谷歌“单身反模式”看到一些反对它的论点。 我认为它没有任何内在的错误,但它只是一个暴露一些全球资源/数据的机制,所以确保这是最好的方法。 特别是我发现dependency injection更有用,特别是如果你也使用unit testing,因为DI允许你使用模拟资源进行testing。
不要忘记,Singleton对于加载它的Classloader只是一个Singleton。 如果你使用多个装载器(Container),每个装载器都有自己的Singleton版本。
我对一些暗示DI是使用单例的替代方法感到困惑, 这些是不相关的概念。 您可以使用DI来注入单例或非单例(例如每个线程)实例。 至less在使用Spring 2.x的时候是这样,我不能说其他的DI框架。
所以我对OP的回答将是(除了最微不足道的示例代码外):
- 然后使用像Spring这样的DI框架
- 把它作为你的DIconfiguration的一部分,不pipe你的依赖是单例,请求作用域,会话作用域还是其他。
这种方法给你一个很好的解耦(因此灵活和可testing)的架构,是否使用单例是一个容易可逆的实现细节(当然你提供的任何单例是线程安全的)。
在写作之前真的要考虑为什么你需要一个单身人士。 有一个关于使用它们的准宗教性的争论,如果你在Java中使用谷歌单例,你可以很容易地find它们。
我个人尽量避免单身人士出于多种原因,其中大部分可以通过谷歌单身人士find。 我觉得很多时候单身人士都被滥用,因为每个人都很容易理解单身人士,他们被用来把“全局”数据转化为面向对象devise的机制,并且因为很容易规避对象生命周期pipe理(或真正考虑如何从B里面做A)。 看一下像控制反转(IoC)或dependency injection(DI)这样的好的中间场景。
如果你真的需要一个,那么维基百科有一个正确实施单身人士的好例子。
以下是3种不同的方法
1)枚举
/** * Singleton pattern example using Java Enumj */ public enum EasySingleton{ INSTANCE; }
2)双重检查locking/延迟加载
/** * Singleton pattern example with Double checked Locking */ public class DoubleCheckedLockingSingleton{ private static volatile DoubleCheckedLockingSingleton INSTANCE; private DoubleCheckedLockingSingleton(){} public static DoubleCheckedLockingSingleton getInstance(){ if(INSTANCE == null){ synchronized(DoubleCheckedLockingSingleton.class){ //double checking Singleton instance if(INSTANCE == null){ INSTANCE = new DoubleCheckedLockingSingleton(); } } } return INSTANCE; } }
3)静态工厂方法
/** * Singleton pattern example with static factory method */ public class Singleton{ //initailzed during class loading private static final Singleton INSTANCE = new Singleton(); //to prevent creating another instance of Singleton private Singleton(){} public static Singleton getSingleton(){ return INSTANCE; } }
我使用Spring框架来pipe理我的单身人士。 它没有强制实现类的“单一性”(如果涉及多个类加载器,则无法真正做到这一点),而是提供了一种非常简单的方法来构build和configuration不同的工厂以创build不同types的对象。
版本1:
public class MySingleton { private static MySingleton instance = null; private MySingleton() {} public static synchronized MySingleton getInstance() { if(instance == null) { instance = new MySingleton(); } return instance; } }
延迟加载,线程安全阻塞, synchronized
性能低。
版本2:
public class MySingleton { private MySingleton() {} private static class MySingletonHolder { public final static MySingleton instance = new MySingleton(); } public static MySingleton getInstance() { return MySingletonHolder.instance; } }
延迟加载,线程安全,无阻塞,高性能。
维基百科有一些单例的例子 ,也是在Java中。 Java 5实现看起来相当完整,并且是线程安全的(应用双重检查locking)。
如果你不需要延迟加载,那就试试吧
public class Singleton { private final static Singleton INSTANCE = new Singleton(); private Singleton() {} public static Singleton getInstance() { return Singleton.INSTANCE; } protected Object clone() { throw new CloneNotSupportedException(); } }
如果你想延迟加载,并希望你的单例是线程安全的,请尝试双重检查模式
public class Singleton { private static Singleton instance = null; private Singleton() {} public static Singleton getInstance() { if(null == instance) { synchronized(Singleton.class) { if(null == instance) { instance = new Singleton(); } } } return instance; } protected Object clone() { throw new CloneNotSupportedException(); } }
由于双重检查模式不能保证工作(由于编译器的问题,我不知道更多关于这一点),你也可以尝试同步整个getInstance方法或为所有的单例创build一个registry。
我会说Enum单身人士
在Java中使用枚举的单例通常是声明枚举单例的方法。 枚举单例可能包含实例variables和实例方法。 为了简单起见,还要注意,如果您使用的是任何实例方法,而不是您需要确保该方法的线程安全性(如果完全影响对象的状态的话)。
枚举的使用非常容易实现,并且对于可序列化的对象没有任何缺点,这些对象必须以其他方式绕过。
/** * Singleton pattern example using Java Enum */ public enum Singleton { INSTANCE; public void execute (String arg) { //perform operation here } }
你可以通过Singleton.INSTANCE
访问它,比在Singleton上调用getInstance()
方法容易得多。
1.12枚举常量的序列化
枚举常量序列化不同于普通的可序列化或可外部化的对象。 枚举常量的序列化forms仅由其名称组成; 常量的字段值不存在于表单中。 要序列化枚举常量,
ObjectOutputStream
将写入由枚举常量的名称方法返回的值。 为了反序列化一个枚举常量,ObjectInputStream
从stream中读取常量名称; 然后通过调用java.lang.Enum.valueOf
方法获得反序列化常量,将常量的枚举types与接收的常量名称一起作为parameter passing。 像其他可串行化或可外部化的对象一样,枚举常量可以作为序列化stream中随后出现的后向引用的目标。枚举常量序列化的过程不能自定义:在序列化和反序列化过程中,由枚举types定义的任何类特定的
writeObject
,readObject
,readObjectNoData
,writeReplace
和readResolve
方法都将被忽略。 同样,任何serialPersistentFields
或serialVersionUID
字段声明也被忽略 – 所有枚举types都有一个固定的serialVersionUID
0L
。 为枚举typeslogging可序列化的字段和数据是不必要的,因为发送的数据types没有变化。从Oracle文档引用
传统单例的另一个问题是一旦你实现Serializable
接口,它们不再是单例,因为readObject()
方法总是返回一个新的实例,像Java中的构造函数。 这可以通过使用readResolve()
并通过用下面的单例replace来丢弃新创build的实例来避免
// readResolve to prevent another instance of Singleton private Object readResolve(){ return INSTANCE; }
如果你的Singleton类保持状态,这可能会变得更加复杂,因为你需要使它们变成瞬态的,但是在Enum Singleton中,序列化由JVM保证。
好阅读
- 单例模式
- 枚举,单身和反序列化
- 双选locking和单例模式
There are 4 ways to create a singleton in java. 1- eager initialization singleton public class Test{ private static final Test test = new Test(); private Test(){} public static Test getTest(){ return test; } } 2- lazy initialization singleton (thread safe) public class Test { private static volatile Test test; private Test(){} public static Test getTest() { if(test == null) { synchronized(Test.class) { if(test == null){test = new Test(); } } } return test; } 3- Bill Pugh Singleton with Holder Pattern (Preferably the best one) public class Test { private Test(){} private static class TestHolder{ private static final Test test = new Test(); } public static Test getInstance(){ return TestHolder.test; } } 4- enum singleton public enum MySingleton { INSTANCE; private MySingleton() { System.out.println("Here"); } }
如果你需要加载一个类的实例variables,你需要仔细检查一下习惯用法。 如果你需要加载一个静态variables或者一个单例,你需要根据需求持有者惯用法进行初始化 。
另外,如果单例需要可以被seriliazble,所有其他的字段都需要是短暂的,并且需要实现readResolve()方法以保持单例对象不变。 否则,每次对象被反序列化时,都会创build一个新的对象实例。 readResolve()所做的是replace由readObject()读取的新对象,该对象强制将新对象进行垃圾回收,因为没有引用它的variables。
public static final INSTANCE == .... private Object readResolve() { return INSTANCE; // original singleton instance. }
各种方法来使单身物体:
-
根据Joshua Bloch – Enum将是最好的。
-
你也可以使用双重检查locking。
-
Even inner static class can be used.
Enum singleton
The simplest way to implement a Singleton that is thread-safe is using an Enum
public enum SingletonEnum { INSTANCE; public void doSomething(){ System.out.println("This is a singleton"); } }
This code works since the introduction of Enum in Java 1.5
Double checked locking
If you want to code a “classic” singleton that works in a multithreaded environment (starting from Java 1.5) you should use this one.
public class Singleton { private static volatile Singleton instance = null; private Singleton() { } public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class){ if (instance == null) { instance = new Singleton(); } } } return instance ; } }
This is not thread-safe before 1.5 because the implementation of the volatile keyword was different.
Early loading Singleton (works even before Java 1.5)
This implementation instantiates the singleton when the class is loaded and provides thread safety.
public class Singleton { private static final Singleton instance = new Singleton(); private Singleton() { } public static Singleton getInstance() { return instance; } public void doSomething(){ System.out.println("This is a singleton"); } }
This is how to implement a simple singleton
:
public class Singleton { // It must be static and final to prevent later modification private static final Singleton INSTANCE = new Singleton(); /** The constructor must be private to prevent external instantiation */ private Singleton(){}; /** The public static method allowing to get the instance */ public static Singleton getInstance() { return INSTANCE; } }
This is how to properly lazy create your singleton
:
public class Singleton { // The constructor must be private to prevent external instantiation private Singleton(){}; /** The public static method allowing to get the instance */ public static Singleton getInstance() { return SingletonHolder.INSTANCE; } /** * The static inner class responsible for creating your instance only on demand, * because the static fields of a class are only initialized when the class * is explicitly called and a class initialization is synchronized such that only * one thread can perform it, this rule is also applicable to inner static class * So here INSTANCE will be created only when SingletonHolder.INSTANCE * will be called */ private static class SingletonHolder { private static final Singleton INSTANCE = new Singleton(); } }
For JSE 5.0 and above take the Enum approach, otherwise use static singleton holder approach ( (a lazy loading approach described by Bill Pugh). Latter solution is also thread-safe without requiring special language constructs (ie volatile or synchronized).
Another argument often used against Singletons are their testability problems. Singletons are not easily mockable for testing purposes. If this turns out to be a problem, I like to make the following slight modification:
public class SingletonImpl { private static SingletonImpl instance; public static SingletonImpl getInstance() { if (instance == null) { instance = new SingletonImpl(); } return instance; } public static void setInstance(SingletonImpl impl) { instance = impl; } public void a() { System.out.println("Default Method"); } }
The added setInstance
method allows setting a mockup implementation of the singleton class during testing:
public class SingletonMock extends SingletonImpl { @Override public void a() { System.out.println("Mock Method"); } }
This also works with early initialization approaches:
public class SingletonImpl { private static final SingletonImpl instance = new SingletonImpl(); private static SingletonImpl alt; public static void setInstance(SingletonImpl inst) { alt = inst; } public static SingletonImpl getInstance() { if (alt != null) { return alt; } return instance; } public void a() { System.out.println("Default Method"); } } public class SingletonMock extends SingletonImpl { @Override public void a() { System.out.println("Mock Method"); } }
This has the drawback of exposing this functionality to the normal application too. Other developers working on that code could be tempted to use the ´setInstance´ method to alter alter a specific function and thus changing the whole application behaviour, therefore this method should contain at least a good warning in it's javadoc.
Still, for the possibility of mockup-testing (when needed), this code exposure may be an acceptable price to pay.
Might be a little late to the game on this, but there is a lot of nuance around implementing a singleton. The holder pattern can not be used in many situations. And IMO when using a volatile – you should also use a local variable. Let's start at the beginning and iterate on the problem. You'll see what I mean.
The first attempt might look something like this:
public class MySingleton { private static MySingleton INSTANCE; public static MySingleton getInstance() { if (INSTANCE == null) { INSTANCE = new MySingleton(); } return INSTANCE; } ... }
Here we have the MySingleton class which has a private static member called INSTANCE, and a public static method called getInstance(). The first time getInstance() is called, the INSTANCE member is null. The flow will then fall into the creation condition and create a new instance of the MySingleton class. Subsequent calls to getInstance() will find that the INSTANCE variable is already set, and therefore not create another MySingleton instance. This ensures there is only one instance of MySingleton which is shared among all callers of getInstance().
But this implementation has a problem. Multi-threaded applications will have a race condition on the creation of the single instance. If multiple threads of execution hit the getInstance() method at (or around) the same time, they will each see the INSTANCE member as null. This will result in each thread creating a new MySingleton instance and subsequently setting the INSTANCE member.
private static MySingleton INSTANCE; public static synchronized MySingleton getInstance() { if (INSTANCE == null) { INSTANCE = new MySingleton(); } return INSTANCE; }
Here we've used the synchronized keyword in the method signature to synchronize the getInstance() method. This will certainly fix our race condition. Threads will now block and enter the method one at a time. But it also creates a performance problem. Not only does this implementation synchronize the creation of the single instance, it synchronizes all calls to getInstance(), including reads. Reads do not need to be synchronized as they simply return the value of INSTANCE. Since reads will make up the bulk of our calls (remember, instantiation only happens on the first call), we will incur an unnecessary performance hit by synchronizing the entire method.
private static MySingleton INSTANCE; public static MySingleton getInstance() { if (INSTANCE == null) { synchronize(MySingleton.class) { INSTANCE = new MySingleton(); } } return INSTANCE; }
Here we've moved synchronization from the method signature, to a synchronized block that wraps the creation of the MySingleton instance. But does this solve our problem? Well, we are no longer blocking on reads, but we've also taken a step backward. Multiple threads will hit the getInstance() method at or around the same time and they will all see the INSTANCE member as null. They will then hit the synchronized block where one will obtain the lock and create the instance. When that thread exits the block, the other threads will contend for the lock, and one by one each thread will fall through the block and create a new instance of our class. So we are right back where we started.
private static MySingleton INSTANCE; public static MySingleton getInstance() { if (INSTANCE == null) { synchronized(MySingleton.class) { if (INSTANCE == null) { INSTANCE = createInstance(); } } } return INSTANCE; }
Here we issue another check from INSIDE the block. If the INSTANCE member has already been set, we'll skip initialization. This is called double-checked locking.
This solves our problem of multiple instantiation. But once again, our solution has presented another challenge. Other threads might not “see” that the INSTANCE member has been updated. This is because of how Java optimizes memory operations. Threads copy the original values of variables from main memory into the CPU's cache. Changes to values are then written to, and read from, that cache. This is a feature of Java designed to optimize performance. But this creates a problem for our singleton implementation. A second thread — being processed by a different CPU or core, using a different cache — will not see the changes made by the first. This will cause the second thread to see the INSTANCE member as null forcing a new instance of our singleton to be created.
private static volatile MySingleton INSTANCE; public static MySingleton getInstance() { if (INSTANCE == null) { synchronized(MySingleton.class) { if (INSTANCE == null) { INSTANCE = createInstance(); } } } return INSTANCE; }
We solve this by using the volatile keyword on the declaration of the INSTANCE member. This will tell the compiler to always read from, and write to, main memory, and not the CPU cache.
But this simple change comes at a cost. Because we are bypassing the CPU cache, we will take a performance hit each time we operate on the volatile INSTANCE member — which we do 4 times. We double-check existence (1 and 2), set the value (3), and then return the value (4). One could argue that this path is the fringe case as we only create the instance during the first call of the method. Perhaps a performance hit on creation is tolerable. But even our main use-case, reads, will operate on the volatile member twice. Once to check existence, and again to return its value.
private static volatile MySingleton INSTANCE; public static MySingleton getInstance() { MySingleton result = INSTANCE; if (result == null) { synchronized(MySingleton.class) { result = INSTANCE; if (result == null) { INSTANCE = result = createInstance(); } } } return result; }
Since the performance hit is due to operating directly on the volatile member, let's set a local variable to the value of the volatile and operate on the local variable instead. This will decrease the number of times we operate on the volatile, thereby reclaiming some of our lost performance. Note that we have to set our local variable again when we enter the synchronized block. This ensures it is up to date with any changes that occured while we were waiting for the lock.
I wrote an article about this recently. Deconstructing The Singleton . You can find more info on these examples and an example of the "holder" pattern there. There is also a real-world example showcasing the double-checked volatile approach. 希望这可以帮助。
simplest singleton class
public class Singleton { private static Singleton singleInstance = new Singleton(); private Singleton() {} public static Singleton getSingleInstance() { return singleInstance; } }
I still think after java 1.5, enum is the best available singleton implementation available as it also ensures that even in the multi threaded environments – only one instance is created.
public enum Singleton{ INSTANCE; }
and you are done !!!
Have a look at this post.
Java核心库中的GoFdevise模式示例
From the best answer's "Singleton" section,
Singleton (recognizeable by creational methods returning the same instance (usually of itself) everytime)
- java.lang.Runtime#getRuntime()
- java.awt.Desktop#getDesktop()
- java.lang.System#getSecurityManager()
You can also learn the example of Singleton from Java native classes themselves.
To achieve this ( TRUE Singleton) ,
- Make sure that your class is final. Others can't sub-class it and create one more instance
- Make your singleton object as private static final
- Provide private constructor and public getInstance() method.
- Make sure that this Singleton class is loaded by one ClassLoader only
- override readResolve() method and return same instance.
Useful links: All answers in this post +
Singleton_pattern : Initialization-on-demand holder idiom from wikipedia
journaldev article
Demonstration of various methods of achieving Singleton
public final class Singleton{ private static final Singleton instance = new Singleton(); public static Singleton getInstance(){ return instance; } public enum EnumSingleton { INSTANCE; } private Object readResolve() { return instance; } public static void main(String args[]){ System.out.println("Singleton:"+Singleton.getInstance()); System.out.println("Enum.."+EnumSingleton.INSTANCE); System.out.println("Lazy.."+LazySingleton.getInstance()); } } final class LazySingleton { private LazySingleton() {} public static LazySingleton getInstance() { return LazyHolder.INSTANCE; } private static class LazyHolder { private static final LazySingleton INSTANCE = new LazySingleton(); } private Object readResolve() { return LazyHolder.INSTANCE; } }
输出:
Singleton:Singleton@5eb1404f Enum..INSTANCE Lazy..LazySingleton@46fb3d6
Sometimes a simple " static Foo foo = new Foo();
" is not enough. Just think of some basic data insertion you want to do.
On the other hand you would have to synchronize any method that instantiates the singleton variable as such. Synchronisation is not bad as such, but it can lead to performance issues or locking (in very very rare situations using this example. The solution is
public class Singleton { private static Singleton instance = null; static { instance = new Singleton(); // do some of your instantiation stuff here } private Singleton() { if(instance!=null) { throw new ErrorYouWant("Singleton double-instantiation, should never happen!"); } } public static getSingleton() { return instance; } }
Now what happens? The class is loaded via the class loader. Directly after the class was interpreted from a byte Array, the VM executes the static { } – block. that's the whole secret: The static-block is only called once, the time the given class (name) of the given package is loaded by this one class loader.
public class Singleton { private static final Singleton INSTANCE = new Singleton(); private Singleton(){ if (INSTANCE != null) throw new IllegalStateException (“Already instantiated...”); } public synchronized static Singleton getInstance() { return INSTANCE; } }
As we have added the Synchronized keyword before getInstance, we have avoided the race condition in the case when two threads call the getInstance at the same time.