# 单例模式 Singleton

单例模式,顾名思义就是只有一个实例,并且她自己负责创建自己的对象,这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。

例如各种 Manager、各种 Factory

# 实现方式

# 饿汉式 - 实用

  • 先把构造方法设置为私有
  • 提供公共的获取实例的方法
  • JVM 保证线程安全 - JVM 保证每一个 Class 只会 Load 到内存一次
  • 简单实用 -- 推荐
  • 唯一问题 -- 在类加载到内存时就执行初始化
/**
 * 单例模式 -- 饿汉式
 *
 * @author DanielLi
 * @version 2.0.0-RELEASE
 * DateTime 2020/11/6 2:46 下午
 */
public class Manager01 {
   
    /**
     * 初始化实例
     */
    private static final Manager01 INSTANCE = new Manager01();

    /**
     * 定义静态 private 的构造方法, 以避免其他类直接构造
     */
    private Manager01() {}

    /**
     * 提供 public 的获取实例的方法, 以便其他类获取实例
     */
    public static Manager01 getInstance() {
        return INSTANCE;
    }
}
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

测试方法

public static void main(String[] args) {
  Manager01 manager01 = Manager01.getInstance();
  Manager01 manager02 = Manager01.getInstance();

  System.out.println(manager01 == manager02);
}
1
2
3
4
5
6

# 饿汉式 - 静态语句块

基本和饿汉式1一样, 只是把初始化语句改成了静态语句块.

/**
 * 单例模式02 -- 饿汉式2
 *
 * @author DanielLi
 * @version 2.0.0-RELEASE
 * DateTime 2020/11/6 2:49 下午
 */
public class Manager02 {
    private static final Manager02 INSTANCE;
    static {
        INSTANCE = new Manager02();
    }

    private Manager02(){}

    public static Manager02 getInstance(){
        return INSTANCE;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 懒汉式 - 有线程问题

多线程同时访问时, 一个线程判断 if (INSTANCE == null) 则进入初始化, 第一个线程还没有初始化之前, 第二个线程判断, 依然得到 INSTANCE == null 为空, 所以也会进入初始化.

实现方式

/**
 * 单例模式03 -- 懒汉式1
 *
 * @author DanielLi
 * @version 2.0.0-RELEASE
 * DateTime 2020/11/6 2:49 下午
 */
public class Manager03 {
    /**
     * 定义实例但是不初始化
     */
    private static Manager03 INSTANCE;

    /**
     * 静态构造方法
     */
    private Manager03(){}

    /**
     * 获取实例的方法
     */
    public static Manager03 getInstance(){
        
        if (INSTANCE == null) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            
            // 在第一次调用的时候再初始化
            INSTANCE = new Manager03();
        }
        return INSTANCE;
    }
}
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
34
35
36

# 懒汉式 - 加锁

  • lazy loading 也称懒汉式
  • 虽然达到了按需初始化的目的,但却带来线程不安全的问题
  • 可以通过synchronized解决,但也带来效率下降

实现方式

/**
 * 加锁的形式
 *
 * @author DanielLi
 * @version 2.0.0-RELEASE
 * DateTime 2020/11/6 3:14 下午
 */
public class Manager04 {
    private static Manager04 INSTANCE;

    private Manager04() {
    }

    public static synchronized Manager04 getInstance() {
        if (INSTANCE == null) {
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            INSTANCE = new Manager04();
        }
        return INSTANCE;
    }
}
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

因为 getInstance() 是静态的, 这里 synchronized 锁定的是 Manager04.class 对象

# 懒汉式 - 减小同步代码块

妄图通过减小同步代码块的方式提高效率,然后不可行,

判断和锁定不在一起

实现方式

/**
 * 单例模式05 - 懒汉式, 减小同步代码块
 *
 * @author DanielLi
 * @version 2.0.0-RELEASE
 * DateTime 2020/11/6 3:18 下午
 */
public class Manager05 {
    private static Manager05 INSTANCE;

    private Manager05() {
    }

    public static Manager05 getInstance() {
        if (INSTANCE == null) {
            //妄图通过减小同步代码块的方式提高效率,然后不可行
            synchronized (Manager05.class) {
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                INSTANCE = new Manager05();
            }
        }
        return INSTANCE;
    }
}
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

# 懒汉式 - 双重检查

  • 定义实例时 volatile 的必要性: 这里主要是为了防止 JIT 进行编译的时候对指令进行重排序
  • 双重检查的必要性: 第一次检查被执行到的几率更大, 这样就不用每次获取实例时都加锁

实现方式

/**
 * 单例模式 - 懒汉式(双重检查)
 *
 * @author DanielLi
 * @version 2.0.0-RELEASE
 * DateTime 2020/11/6 3:22 下午
 */
public class Manager06 {

    /**
     * 定义实例对象
     */
    private static volatile Manager06 INSTANCE;

    /**
     * 私有构造方法
     */
    private Manager06() {
    }

    /**
     * 初始化方法
     */
    public static Manager06 getInstance() {
        // 第一次检查
        if (INSTANCE == null) {
            // 加锁
            synchronized (Manager06.class) {
                // 第二次检查
                if(INSTANCE == null) {
                    try {
                        Thread.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    
                    // 初始化对象
                    INSTANCE = new Manager06();
                }
            }
        }
        return INSTANCE;
    }
}
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
34
35
36
37
38
39
40
41
42
43
44

# 懒汉式 - 静态内部类

  • 加载外部类时不会加载内部类,这样可以实现懒加载
  • JVM 保证线程安全 -- JVM 保证每一个 Class 只会 Load 到内存一次

实现方式

/**
 * 单例模式 - 懒汉式(静态内部类)
 * JVM 保证单例
 * 加载外部类时不会加载内部类,这样可以实现懒加载
 *
 * @author DanielLi
 * @version 2.0.0-RELEASE
 * DateTime 2020/11/6 3:30 下午
 */
public class Manager07 {

    /**
     * 定义 private 的构造方法
     */
    private Manager07() {
    }

    /**
     * 定义静态内部类
     */
    private static class Manager07Holder {
        // 在静态内部类立面定义 INSTANCE
        private final static Manager07 INSTANCE = new Manager07();
    }

    /**
     * 获取实例的方法
     */
    public static Manager07 getInstance() {
        return Manager07Holder.INSTANCE;
    }
}
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

# 枚举单例

  • 不仅可以解决线程同步,还可以防止反序列化
    • 枚举单例不会被反序列化的原因: 枚举类是没有构造方法
    • 枚举反编译后是一个 AbstractClass

实现方式

/**
 * 单例模式 - 枚举
 *
 * @author DanielLi
 * @version 2.0.0-RELEASE
 * DateTime 2020/11/6 3:34 下午
 */
public enum Manager08 {

    INSTANCE;

	  /**
     * 测试示例
     */
    public static void main(String[] args) {
        for(int i=0; i<100; i++) {
            new Thread(()->{
                System.out.println(Manager08.INSTANCE.hashCode());
            }).start();
        }
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
上次更新时间: 2020/11/7 下午10:53:23