模式说明

某些场景要求一个对象只能有一个实例,可以通过单例模式实现。由于只能有一个实例,因此该类的构造器必须用private修饰来禁止通过new操作符来新建实例。类内持有一个private修饰的本类类型的实例,又因为只能有一个实例,所以使其为类成员,如该类类名Singleton,可声明为:
private static Singleton instance;
通过类内的一个public修饰的getIntance方法来获取实例,该方法也是static的。根据实例产生的时机分为饿汉模式和懒汉模式,懒汉模式中有线程安全和不安全的写法。

饿汉模式
类属性Singleton instance在声明时就实例化。
private static Singleton instance = new Singleton();

懒汉模式
类属性Singleton只声明但不实例化,将实例化动作放到getInstance方法中,则只有调用getInstance时才能获取Singleton的实例。懒汉模式因为在调用getInstance时才获得实例,存在多线程竞争的问题,因此可以结合volatile(避免指令重排)和synchronized(保证原子性)以双锁检测方式将Singleton写成线程安全的类。

本示例演示饿汉写法,非线程安全懒汉写法和双锁检测线程安全写法。

结构

单例类
持有一个本类的类属性,维护一个getInstance方法。

代码演示

  1. package com.yukiyama.pattern.creation;
  2. /**
  3. * 单例模式
  4. */
  5. public class SingletonDemo {
  6. public static void main(String[] args) {
  7. SingletonSimple s1 = SingletonSimple.getInstance();
  8. SingletonSimple s2 = SingletonSimple.getInstance();
  9. // 输出“true”,说明s1与s2是同一个实例
  10. System.out.println(s1 == s2);
  11. SingletonHungry s3 = SingletonHungry.getInstance();
  12. SingletonHungry s4 = SingletonHungry.getInstance();
  13. // 输出“true”,说明s3与s4是同一个实例
  14. System.out.println(s3 == s4);
  15. Singleton s5 = Singleton.getInstance();
  16. Singleton s6 = Singleton.getInstance();
  17. // 输出“true”,说明s3与s4是同一个实例
  18. System.out.println(s5 == s6);
  19. }
  20. }
  21. /**
  22. * 饿汉模式
  23. * 在类加载时创建常量化实例,不存在多线程导致可能出现多个实例的问题
  24. */
  25. class SingletonHungry{
  26. // 在定义SingletonHungry类型的属性时直接实例化,类内可以访问private构造器
  27. private static final SingletonHungry instance = new SingletonHungry();
  28. // 将构造器声明为private,外部无法用new获取
  29. private SingletonHungry() {}
  30. // 外部通过一个public的getInstance()方法获取该类实例
  31. public static SingletonHungry getInstance() {
  32. return instance;
  33. }
  34. }
  35. /**
  36. * 多线程下的双锁检测(Double-Check Locking)单例
  37. * 懒汉模式
  38. */
  39. class Singleton{
  40. // 以volatile修饰
  41. private static volatile Singleton instance;
  42. private Singleton() {}
  43. public static Singleton getInstance() {
  44. // 第一次判断的目的是避免每次getInstance()都加锁
  45. // 若已经存在实例,直接返回
  46. if(instance == null) {
  47. synchronized (Singleton.class) {
  48. // 再次判断是防止两个线程在instance==null时
  49. // 同时进入第一个if内,由于加锁,其中一个先new了
  50. // 实例,此时必须再判断一次防止第二个也new一个实例
  51. if(instance == null) {
  52. instance = new Singleton();
  53. }
  54. }
  55. }
  56. return instance;
  57. }
  58. }
  59. /**
  60. * 懒汉模式
  61. * 非线程安全版
  62. */
  63. class SingletonSimple {
  64. private static SingletonSimple instance;
  65. // 将构造器声明为private,外部无法用new获取
  66. private SingletonSimple() {}
  67. // 外部通过一个public的getInstance()方法获取该类实例
  68. public static SingletonSimple getInstance() {
  69. // 每次获取前判断该类实例是否已存在,若无则new一个
  70. if (instance != null) {
  71. instance = new SingletonSimple();
  72. }
  73. return instance;
  74. }
  75. }