singleton pattern
参考文章:https://howtodoinjava.com/design-patterns/creational/singleton-design-pattern-in-java/
1、加载时初始化
这种模式会在实际需要之前被创建出来,大部分是在系统启动的时候。加载初始化被创建不论实际是否需要
1 | public class EagerSingleton { |
2、延迟初始化
在计算机编程中,延迟初始化是延迟创建对象,运算或者一些昂贵的线程,知道第一次被需要的一种策略,这种单例模式,它直到第一次被请求的时候,才会创建实例。
1 | public final class LazySingleton { |
上面这种方式会在使用实例变量的时候检查实例是否已经创建。如果实例没有创建,例如实例为null,它将创建实例并返回它的引用。如果实例已经创建好了,它将直接返回实例的引用。
但是,这种方法有它的缺点。现在有两个线程T1和T2。两个同时创建对象并且检查“instance==null”。两个线程的实例变量都为null,然后他们都会创建新的实例。他们从而都进入了synchronized 块并且创建实例。最后,在我们应用中有两个实例。
这个错误可以用doule-checked locking解决。
1 | public class LazySingleton2 { |
请确保使用volatile
关键字在实例变量上,否则你可能运行乱序导致错误,实例在时间创建之前返回引用。jvm只会分配内存,构造方法还没有执行。这种情况,其他线程可能引用未初始化的对象,然后抛出了npe,甚至可能导致整个应用程序崩溃。
3、静态代码块初始化
如果你对类加载顺序有所了解,则可以使用在加载类期间执行静态块,甚至在调用构造函数之前。我们可以将整个特性用于单例模式。
1 | public class StaticBlockSingleton { |
这种方法有一个缺点。在一个类中有5个静态属性,应用程序只需要2到3个,我们创建了多余的实例。所以这种初始化,你可能创建你不需要的实例。
4、静态内部类
1 | public class BillPughSingleton { |
这种方式,知道我们需要实例的时候,LazyHolder类不会初始化,直到需要的时候,我们然后可以使用BillPughSingleton类的其他静态变量。
5、枚举
1 | public enum EnumSingleton { |
6、序列化
1 | public class DemoSingleton implements Serializable { |
1 | public class SerializationTest { |
1 | readResolve is used for replacing the object read from the stream. The only use I've ever seen for this is enforcing singletons; when an object is read, replace it with the singleton instance. This ensures that nobody can create another instance by serializing and deserializing the singleton. |