概述

​ 所谓单例设计模式,就是采用一定的方法保证整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个获取其对象实例的方法(静态方法)。

​ 单例模式具体有八种方式:

  • 饿汉式(静态常量)
  • 饿汉式(静态代码块)
  • 懒汉式(线程不安全)
  • 懒汉式(线程安全,同步方法)
  • 懒汉式(线程不安全,同步代码块)
  • 双重检查
  • 静态内部类
  • 枚举

饿汉式(静态常量)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* @author kongliufeng
*/
public class Singleton {
private final static Singleton singleton = new Singleton();
/**
* 私有化构造器
*/
private Singleton() {

}
/**
* 对外提供访问
*/
public static Singleton getInstance(){
return singleton;
}
}

饿汉式(静态代码块)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* @author kongliufeng
*/
public class Singleton {

private static Singleton singleton= null;

static {
singleton = new Singleton();
}

public static Singleton getInstance(){
return singleton;
}
}

懒汉式(线程不安全)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* 懒汉式
* 线程不安全
* 起到懒加载的效果, 但是只能在单线程下使用。
* @author kongliufeng
*/
public class Singleton {

private static Singleton singleton;

private Singleton() {

}

public static Singleton getInstance(){
if(singleton==null){
singleton = new Singleton();
}
return singleton;
}
}

懒汉式(线程安全,同步方法)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* 懒汉式-同步方法
* 方法上加锁. 线程安全
*
*
* @author kongliufeng
* @create 2019-08-16
*/
public class Singleton {
private static Singleton singleton;

private Singleton() {
}

public static synchronized Singleton getInstance(){
if(singleton==null){
return singleton = new Singleton();
}
return singleton;
}
}
  • 解决了线程安全问题
  • 效力太低了,每个线程在想获得类得实体时候,执行 getInstance()方法都要进行同步。而其实这个方法只要执行一次实例化就够了,后面的想要获取该类实例,直接return就行了。方法进行同步效率太低
  • 在实际开发中,不建议使用。

懒汉式(线程不安全,同步代码块)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* @author kongliufeng
* @create 2019-08-16
*/
public class Singleton {
private static Singleton singleton;

private Singleton() {
}

public static Singleton getInstance(){
// 如果对象为空,则创建。不为空,则返回
if(singleton==null){
//加入同步代码块
synchronized (Singleton.class){
singleton = new Singleton();
}
}
return singleton;
}
}
  • 虽然加了同步关键字,但同样是线程不安全的,因为可能有几个线程同时进入if(singleton == nul)的判断,然后再同步创建出多个实例出来。

双重检查

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
/**
* @author kongliufeng
*/
public class Singleton {
private static volatile Singleton singleton;

private Singleton(){

}

public static Singleton getInstance(){
/**
* 此处检测singletonMode == null,是为了防止当singletonMode已经初始化后,
* 还会继续调用同步锁,造成不必要的损耗
*/
if(singleton==null){
// 加锁目的,防止多线程同时进入造成对象多次实例化
synchronized (Singleton.class){
// 为了在null的情况下创建实例,防止不必要的损耗
if (singleton != null) {
singleton = new Singleton();
}
}
}
// 返回实例对象
return singleton;
}
}

  • 第一次判断是为了验证是否创建对象,判断为了避免不必要的同步。

  • 第二次判断是为了避免重复创建单例,因为可能会存在多个线程通过了第一次判断在等待锁,来创建新的实例对象。

    volatile作用?

    • 保证对所有线程的可见性。
      • 读volatile:每当子线程某一语句要用到volatile变量时,都会从主线程重新拷贝一份,这样就保证子线程的会跟主线程的一致。
      • 写volatile: 每当子线程某一语句要写volatile变量时,都会在读完后同步到主线程去,这样就保证主线程的变量及时更新。
    • 禁止指令重排序优化。
      • 由于 volatile禁止对象创建时指令之间重排序,所以其他线程不会访问到一个未初始化的对象,从而保证安全性。

    为什么要使用volatile关键字?

    ​ singleton= new Singleton(); 具体机器码指令执行操作如下:

    1. 分配内存地址

    2. 对象进行实例化

    3. 变量singleton赋值

      java代码编程成.class 文件,到最后操作系统执行的时候,会对指令进行优化,进行重排序。会出现1-3-2的情况,这种情况下,对象为实例化情况下, 另一个线程过来后判断对象是否为空,发现对象不为空,直接访问对象,实际上对象还未实例化完毕,生产上就会造成异常。

静态内部类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* @author kongliufeng
* @create 2019-08-16
*/
public class Singleton {

private static Singleton singleton;

private Singleton(){}

private static Singleton getInstance(){
return SingletonInstance.INSTANCE;
}

private static class SingletonInstance{
private static final Singleton INSTANCE = new Singleton();
}

}

枚举

1
2
3
4
5
6
/**
* @author kongliufeng
*/
public enum Singleton {
INSTANCE;
}