前言

单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。 这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象

单例模式实现

1、首先我们得有个SingleObj类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class SingleObj {
private static SingleObj instance = new SingleObj();//创建一个SingleObj

// 使用私有构造方法,外部就不能直接创建改类
private SingleObj(){}

// 通过该方法获取到对象实例
public static SingleObj getInstance(){
return instance;
}

public void sayHello(){
System.out.println("Hello,SingleObj!!!");
}
}

2、创建SingleObj对象并且调用sayHello()方法

1
2
3
4
5
6
public class App {
public static void main(String[] args) {
SingleObj singleObj = SingleObj.getInstance();
singleObj.sayHello();
}
}
1
控制台输出:Hello,SingleObj!!!

常见的5种写法

1、懒汉式

  • 缺点:线程不安全
  • 实现难度:易
  • 是否Lazy:是
  • 是否多线程安全:否
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class SingleLanHan {
private SingleLanHan(){}

private static SingleLanHan instance;

public static SingleLanHan getInstance(){
if(instance == null){
return new SingleLanHan();
}
return instance;
}

public void sayHi(){
System.out.println("Hi,SingleLanHan!!!");
}
}

2、赖汉式

  • 优点:第一次调用才初始化,避免浪费内存
  • 缺点:必须加锁 synchronized 才能保证单例,但加锁会影响效率。getInstance() 的性能对应用程序不是很关键(该方法使用不太频繁)。
  • 实现难度:易
  • 是否Lazy:是
  • 是否多线程安全:是
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class SingleLanHanSave {
private SingleLanHanSave(){}

private static SingleLanHanSave instance;

public static synchronized SingleLanHanSave getInstance(){
if(instance == null){
return new SingleLanHanSave();
}
return instance;
}

public void sayHi(){
System.out.println("Hi,SingleLanHanSave!!!");
}
}

3、饿汉式(比较常用)

  • 优点:没有加锁,执行效率会提高
  • 缺点:类加载时就初始化,浪费内存
  • 实现难度:易
  • 是否Lazy:否
  • 是否多线程安全:是
1
2
3
4
5
6
7
8
9
10
public class ErHan {
private static ErHan instance = new ErHan();
private ErHan(){}
public static ErHan getInstance(){
return instance;
}
public void sayHello(){
System.out.println("Hello,ErHan!!!");
}
}

4、双检锁/双重校验锁(DCL,即 double-checked locking)

  • 优点:这种方式采用双锁机制,安全且在多线程情况下能保持高性能。
  • 实现难度:较复杂
  • 是否Lazy:是
  • 是否多线程安全:是
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class SingleDCL {
private volatile static SingleDCL instance;

private SingleDCL(){}

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

public void sayHello(){
System.out.println("Hello,SingleDCL!!!");
}
}

5、枚举

  • 实现难度:易
  • 是否Lazy:否
  • 是否多线程安全:是
1
2
3
4
5
6
public enum SingleEnum {
INSTANCE;
public void sayHi(){
System.out.println("Hi,SingleEnum!!!");
}
}

描述:这种实现方式还没有被广泛采用,但这是实现单例模式的最佳方法。它更简洁,自动支持序列化机制,绝对防止多次实例化。这种方式是 Effective Java 作者 Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还自动支持序列化机制,防止反序列化重新创建新的对象,绝对防止多次实例化。不过,由于 JDK1.5 之后才加入 enum 特性,用这种方式写不免让人感觉生疏,在实际工作中,也很少用。不能通过 reflection attack 来调用私有构造方法。

总结

先对上面的代码进行总的调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class App {
public static void main(String[] args) {
//懒汉式(线程不安全)
SingleLanHan singleLanHan = SingleLanHan.getInstance();
singleLanHan.sayHi();

//懒汉式(线程安全)
SingleLanHanSave singleLanHanSave = SingleLanHanSave.getInstance();
singleLanHanSave.sayHi();

//饿汉式
SingleErHan singleErHan = SingleErHan.getInstance();
singleErHan.sayHello();

//双检锁/双重校验锁
SingleDCL singleDCL = SingleDCL.getInstance();
singleDCL.sayHello();

//枚举
SingleEnum singleEnum = SingleEnum.INSTANCE;
singleEnum.sayHi();
}
}
1
2
3
4
5
Hi,SingleLanHan!!!
Hi,SingleLanHanSave!!!
Hello,ErHan!!!
Hello,SingleDCL!!!
Hi,SingleEnum!!!

总的来说,个人是比较推荐的是饿汉式和枚举,但常用的还是 饿汉式。凡是都有例外,按需求去实现。

网上有很多设计模式的文章,我这里只是对网络上文章的学习而做个总结,如有建议或疑问请通过以下公众号方式发送消息。