一、概述
- 单例模式:就是采取一定的方法,保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法(静态方法)
二、饿汉式
- 构造器私有化、类的内部创建对象(在类第一次加载的时候就创建了)、向外暴露一个静态的公共方法
public class Singleton {
private final static Singleton instance = new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
return instance;
}
}
public class Singleton2 {
private final static Singleton2 instance;
static {
instance = new Singleton2();
}
private Singleton2() {
}
public static Singleton2 getInstance() {
return instance;
}
}
- 优点:写法简单、类加载时就完成了实例化、避免了线程同步问题
- 缺点:没达到Lazy Loading 的效果,如果从始至终都没有使用过这个实例,就会造成内存浪费
三、懒汉式
1. 不推荐例子
- 提供一个
getInstance()
方法,只有在第一次调用的时候,才创建instance
public static Singleton2 getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
- 优点:Lazy Loading,但只能在单线程时使用
- 缺点:如果在多线程的情况下,一个线程刚判断完
if (instance == null) {}
,还没来得及实例化对象,另一个线程又进行判断。会导致两个线程同时进入if
内部,创建两个实例化对象。
2. 饿汉式(同步方法实现)
public class Singleton3 {
private static Singleton3 instance;
private Singleton3() {
}
public static synchronized Singleton3 getInstance() {
if (instance == null) {
instance = new Singleton3();
}
return instance;
}
}
- 优点:解决了线程安全问题
- 缺点:效率低,实际上只需要在第一次创建的时候同步,后面获取对象的时候,不需要同步。这个方法,使得获取对象的时候,需要线程同步。
3. 懒汉式(双重检查)推荐使用
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;
}
}
- 通过两次
if
,保证在非第一次调用时,直接不进if
,无需线程同步。
- 而若两个线程同时进入
if
的时候,使用同步代码块,只能由一个线程执行对象实例化,而当第二个线程再次进入同步代码块的时候,会发现对象已经被实例化,无需再创建。
volatile
可以禁止重排序,防止出现空指针异常问题。
- 优点:延迟加载,效率较高、线程安全
4. 懒汉式(静态内部类)推荐使用
public class Singleton5 {
private Singleton5() {
}
private static class SingletonInstance {
private final static Singleton5 INSTANCE = new Singleton5();
}
public static Singleton5 getInstance() {
return SingletonInstance.INSTANCE;
}
}
- 通过采用类装载机制的机制保证实例化实例时只有一个线程
- 静态内部类在
Singleton5
类加载时不会被加载,只有在调用getInstance()
方法被调用时,才会被加载
- 优点:避免了线程不安全,利用静态内部类特点实现延迟加载,效率高
四、枚举类实现
public enum Singleton6 {
INSTANCE;
public void sayOK() {
System.out.println("ok");
}
}
public class Test {
public static void main(String[] args) {
Singleton6 instance = Singleton6.INSTANCE;
Singleton6 instance2 = Singleton6.INSTANCE;
System.out.println(instance == instance2);
instance.sayOK();
}
}
- 枚举类本身就是一个很好的单例模式,并且可以使用内部的方法。
- 优点:避免多线程同步问题,而且还能防止反序列化重新创建新的对象