饿汉法
顾名思义,饿汉法就是在第一次引用该类的时候就创建对象实例,而不管实际是否需要创建。代码如下:
1 2 3 4 5 6 7 | public class Singleton { private static Singleton = new Singleton(); private Singleton() {} public static getSignleton(){ return singleton; } } |
这样做的好处是编写简单,但是无法做到延迟创建对象。但是我们很多时候都希望对象可以尽可能地延迟加载,从而减小负载,所以就需要下面的懒汉法:
懒汉法:单线程写法
1 2 3 4 5 6 7 8 | public class Singleton { private static Singleton singleton = null ; private Singleton(){} public static Singleton getSingleton() { if (singleton == null ) singleton = new Singleton(); return singleton; } } |
懒汉法:考虑线程安全的写法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | public class Singleton { private static volatile Singleton singleton = null ; private Singleton(){} public static Singleton getSingleton(){ synchronized (Singleton. class ){ if (singleton == null ){ singleton = new Singleton(); } } return singleton; } } |
懒汉法:兼顾线程安全和效率的写法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | public class Singleton { private static volatile Singleton singleton = null ; private Singleton(){} public static Singleton getSingleton(){ if (singleton == null ){ synchronized (Singleton. class ){ if (singleton == null ){ singleton = new Singleton(); } } } return singleton; } } |
这种写法被称为“双重检查锁”,在单例中new的情况非常少,绝大多数都是可以并行的读操作。因此在加锁前多进行一次null检查就可以减少绝大多数的加锁操作,执行效率提高的目的也就达到了。
懒汉法:静态内部类法
那么,有没有一种延时加载,并且能保证线程安全的简单写法呢?我们可以把Singleton实例放到一个静态内部类中,这样就避免了静态实例在Singleton类加载的时候就创建对象,并且由于静态内部类只会被加载一次,所以这种写法也是线程安全的:
1 2 3 4 5 6 7 8 9 10 11 | public class Singleton { private static class Holder { private static Singleton singleton = new Singleton(); } private Singleton(){} public static Singleton getSingleton(){ return Holder.singleton; } } |
但是,上面提到的所有实现方式都有两个共同的缺点:
- 都需要额外的工作(Serializable、transient、readResolve())来实现序列化,否则每次反序列化一个序列化的对象实例时都会创建一个新的实例。
- 可能会有人使用反射强行调用我们的私有构造器(如果要避免这种情况,可以修改构造器,让它在创建第二个实例的时候抛异常)。
枚举写法
当然,还有一种更加优雅的方法来实现单例模式,那就是枚举写法:
1 2 3 4 5 6 7 8 9 10 | public enum Singleton { INSTANCE; private String name; public String getName(){ return name; } public void setName(String name){ this .name = name; } } |
使用枚举除了线程安全和防止反射强行调用构造器之外,还提供了自动序列化机制,防止反序列化的时候创建新的对象。