前言
单例模式(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(); 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!!!
|
总的来说,个人是比较推荐的是饿汉式和枚举,但常用的还是 饿汉式。凡是都有例外,按需求去实现。
网上有很多设计模式的文章,我这里只是对网络上文章的学习而做个总结,如有建议或疑问请通过以下公众号方式发送消息。