1.单例模式
- 单例模式限制了一个类的实例化,并确保java虚拟机中只存在一个类的实例。
- 单例类必须提供一个全局访问点来获取类的实例。
- 单例模式用于日志记录,驱动程序对象,缓存和线程池。
- Singleton设计模式也用于其他设计模式,如Abstract Factory,Builder,Prototype,Facade等。
2.要点
- 私有的构造函数
- 公共静态方法,返回类的实例,这是外部世界获取单例类的实例的全局访问点。
3.饿汉式单例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| package com.hexsmith.design.pattern.creational.singleton;
public class EagerInitializedSingleton { private static final EagerInitializedSingleton instance = new EagerInitializedSingleton(); private EagerInitializedSingleton(){}
public static EagerInitializedSingleton getInstance(){ return instance; } }
|
这种在初始化时,Singleton类的实例是在类加载时创建的,这是创建单例类的最简单的方法,但它有一个缺点即使客户端应用程序可能没有使用它也会创建实例。
4.懒汉式单例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| package com.hexsmith.design.pattern.creational.singleton;
public class LazyInitializedSingleton { private static LazyInitializedSingleton instance; private LazyInitializedSingleton(){} public static LazyInitializedSingleton getInstance(){ if (instance == null){ instance = new LazyInitializedSingleton(); } return instance; }
}
|
上面的实现对单线程环境工作良好,但是当涉及到多线程系统时,如果多个线程同时处于if循环内部,则会导致问题。它会破坏单例模式,两个线程都会得到单例类的不同实例。
5.静态代码块实现单例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| package com.hexsmith.design.pattern.creational.singleton;
public class StaticBlockSingleton { private static StaticBlockSingleton instance; private StaticBlockSingleton(){} static{ try{ instance = new StaticBlockSingleton(); }catch (Exception e) { throw new RuntimeException("Exception occured in creating singleton instance"); } } public static StaticBlockSingleton getInstance(){ return instance; }
}
|
6.静态内部类实现单例
在Java 5之前,Java内存模型存在很多问题,以上方法在某些情况下失败,因为太多的线程试图同时获得Singleton类的实例。所以Bill Pugh提出了一种使用内部静态帮助类来创建Singleton类的另一种方法。Bill Pugh Singleton的实现是这样的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| package com.hexsmith.design.pattern.creational.singleton;
public class BillPughSingleton { private BillPughSingleton() {} private static class singletonHelper { private static final BillPughSingleton instance = new BillPughSingleton(); } public static BillPughSingleton getInstance() { return singletonHelper.instance; }
}
|
注意包含单例类的实例的私有内部静态类。加载单例类时,SingletonHelper类不会加载到内存中,只有当有人调用getInstance方法时,才会加载此类,并创建Singleton类实例。这是Singleton类最广泛使用的方法,因为它不需要同步。
7.线程安全的单例模式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| package com.hexsmith.design.pattern.creational.singleton;
public class ThreadSafeSingleton {
private static ThreadSafeSingleton instance;
private ThreadSafeSingleton() { }
public static synchronized ThreadSafeSingleton getInstance() { if (instance == null) { instance = new ThreadSafeSingleton(); } return instance; }
public static ThreadSafeSingleton getInstanceUsingDoubleLocking() { if (instance == null) { synchronized (ThreadSafeSingleton.class) { if (instance == null) { instance = new ThreadSafeSingleton(); } } } return instance; } }
|
这种方式实现了线程安全的单例模式,但是在高并发场合使用时性能很糟糕。所以不推荐在高并发环境中使用
8.单例模式序列化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| package com.hexsmith.design.pattern.creational.singleton;
import java.io.Serializable;
public class SerializedSingleton implements Serializable {
private static final long serialVersionUID = -7308821136820448697L; private SerializedSingleton(){}
private static class SingletonHelper{ private static final SerializedSingleton instance = new SerializedSingleton(); } protected Object readResolve() { return getInstance(); }
public static SerializedSingleton getInstance(){ return SingletonHelper.instance; }
}
|
9.使用反射破坏单例模式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| package com.hexsmith.design.pattern.creational.singleton;
import java.lang.reflect.Constructor;
public class ReflectionSingletonTest { @SuppressWarnings("rawtypes") public static void main(String[] args) { EagerInitializedSingleton instanceOne = EagerInitializedSingleton.getInstance(); EagerInitializedSingleton instanceTwo = null; try { Constructor[] constructors = EagerInitializedSingleton.class.getDeclaredConstructors(); for(Constructor constructor : constructors) { constructor.setAccessible(true); instanceTwo = (EagerInitializedSingleton)constructor.newInstance(); } } catch (Exception e) { e.printStackTrace(); } System.out.println(instanceOne.hashCode()); System.out.println(instanceTwo.hashCode()); }
}
|
10.枚举单例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| package com.hexsmith.design.pattern.creational.singleton;
public enum EnumSingleton {
INSTANCE; public static void doSomething() { } }
|
为了克服Reflection的这种情况,Joshua Bloch建议使用Enum来实现Singleton设计模式,因为Java确保任何枚举值在Java程序中仅实例化一次。由于Java Enum值是全局访问的,单例也是全局可访问的。缺点是枚举类型有点不灵活; 例如,它不允许延迟初始化
11.参考资料