Chenyangqi's blog

设计模式·单例模式

2021-12-18

1、饿汉式

  • 优点:类加载的时候完成实例化,避免了线程同步问题
  • 缺点:在类加载的时候就完成实例化,没有达到Lazy Loading的效果,如果该类未使用则造成了内存浪费

可以通过静态变量和静态代码块两种方式实现,效果一样

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
 public class Singleton1 {
//方式1:通过静态变量方式创建
private static final Singleton1 INSTANCE = new Singleton1();

//方式2:通过静态代码块的方式,效果和方法1一样
// private static final Singleton1 INSTANCE;
//
// static {
// INSTANCE = new Singleton1();
// }

/**
* 私有的构造方法,防止产生多个对象
*/
private Singleton1() {
}

/**
* 通过该方法获得实例
*
* @return
*/
public static Singleton1 getInstance() {
return INSTANCE;
}

public void doSomething() {

}
}

可通过如下方式,在多个线程中创建多个单例对象,查看创建的对象hashCode是否相同判断是否为单例

1
2
3
4
5
6
7
8
9
10
11
12
Executor executor = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
executor.execute(new Runnable() {
@Override
public void run() {
//测试饿汉式单例
Singleton1 singleton1 = Singleton1.getInstance();
Singleton1 singleton2 = Singleton1.getInstance();
System.out.println(singleton1.hashCode() + "\t-----\t" + singleton2.hashCode());
}
});
}

打印结果如下,不同线程下的Singleton1对象都是同一个HashCode,表示是全局单例

1
2
3
4
5
6
7
8
9
10
2007961061	-----	2007961061
2007961061 ----- 2007961061
2007961061 ----- 2007961061
2007961061 ----- 2007961061
2007961061 ----- 2007961061
2007961061 ----- 2007961061
2007961061 ----- 2007961061
2007961061 ----- 2007961061
2007961061 ----- 2007961061
2007961061 ----- 2007961061

2、懒汉式-线程不安全

  • 优点:做到了懒加载
  • 缺点:多线程使用的时候会产生多个实例,实际开发中不要使用这种方案
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     public class SingLeton2 {
    private static SingLeton2 INSTANCE;

    private SingLeton2() {
    }

    public static SingLeton2 getInstance() {
    if (INSTANCE == null) {
    INSTANCE = new SingLeton2();
    }
    return INSTANCE;
    }
    }
    多线程创建此单例hashcode如下,存在hashCode不一致的问题,所有没有做到全局单例
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    1337000531	-----	1337000531
    1337000531 ----- 1337000531
    1337000531 ----- 1337000531
    1337000531 ----- 1337000531
    1337000531 ----- 1337000531
    1523178316 ----- 1523178316
    1337000531 ----- 1337000531
    1337000531 ----- 1337000531
    1337000531 ----- 1337000531
    1337000531 ----- 1337000531

3、懒汉式 线程安全

  • 优点:加了同步,线程安全
  • 缺点:效率太低,开发中不推荐使用这种写法
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
     public class Singleton3 {

    private static Singleton3 INSTANCE;

    private Singleton3() {
    }

    public static synchronized Singleton3 getINSTANCE() {
    if (INSTANCE == null) {
    INSTANCE = new Singleton3();
    }
    return INSTANCE;
    }
    }
    下面这种方式,虽然加锁了但是还是存在线程不安全的问题,同一时刻不同的线程走到if(instance==null)这一步时,还是会创建不同的对象
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    //方式二 线程不安全
    static class Singleton7{
    private static Singleton7 instance;

    private Singleton7(){}

    public static Singleton7 getInstance() {
    if(instance==null){
    synchronized (Singleton7.class){
    instance=new Singleton7();
    }
    }
    return instance;
    }
    }

4、双重检查

  • 优点:线程安全,延迟加载,效率高
  • 开发中推荐使用这种方案
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
      public class Singleton4 {
    private static volatile Singleton4 instance;

    private Singleton4() {
    }

    public static Singleton4 getInstance() {
    if (instance == null) {
    synchronized (Singleton4.class) {
    if (instance == null) {
    instance = new Singleton4();
    }
    }
    }
    return instance;
    }
    }

5、静态内部类

  • 优点:线程安全,也做到了懒加载,开发中推荐使用
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     public class Singleton5 {
    private Singleton5() {
    }

    private static class Singleton5Instance {
    private static final Singleton5 INSTANCE = new Singleton5();
    }

    public static Singleton5 getInstance() {
    return Singleton5Instance.INSTANCE;
    }
    }

6、枚举方式创建单例

  • <>推荐使用这种方式创建单例,项目中推荐使用
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
     public enum Singleton6 {
    INSTANCE;

    public void doSomething() {
    System.out.println("枚举创建");
    }

    private String name;

    public String getName() {
    return name;
    }

    public void setName(String name) {
    this.name = name;
    }
    }

扫描二维码,分享此文章