单例模式。
什么是设计模式: 被反复使用,代码设计经验的总结
使用设计模式目的: 代码的重用,让代码更易于理解,保证代码可靠性
概念
适合场景
分类
- 懒汉式:指全局的单例实例在第一次被使用时构建
- 饿汉式:指全局的单例实例在类装载时构建
- 静态内部类: 在静态内部类中声明一个instance
步骤
- 构造器私有化
- 创建类的唯一实例,使用private static 修饰
- 提供一个获取实例的public方法
- enum枚举
饿汉式(线程安全)
1 2 3 4 5 6 7 8 9 10 11 12
| class Singleton{ private static Singleton instance = new Singleton();
private Singleton(){ }
public Singleton getInstance(){ return instance; }
}
|
懒汉式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| class Singleton{ private static Singleton instance ;
private Singleton(){ }
public Singleton getInstance(){ if(instance==null){ instance = new Singleton(); } return instance; }
}
|
上面这种getInstance有致命的缺陷,就是无法保证线程安全。而给整个方法加synchronized效率不高。考虑到同步操作只需要在第一次调用时才被需要,即第一次创建单例实例对象时。这就引出了双重检验锁。
双重检验锁
是一种使用同步块加锁的方法。程序员称其为双重检查锁,因为会有两次检查 instance == null,一次是在同步块外,一次是在同步块内。为什么在同步块内还要再检验一次?因为可能会有多个线程一起进入同步块外的 if,如果在同步块内不进行二次检验的话就会生成多个实例了。
1 2 3 4 5 6 7 8 9
| public Singleton getInstance(){ if(instance==null){ synchronized(Singleton.class){ if(instance==null) instance = new Singleton(); } } return instance; }
|
静态内部类
1 2 3 4 5 6 7 8 9 10
| class Singleton{ private static class SingletonHolder{ private static final Singleton instance = new Singleton(); } private Singleton(){}
public static final Singleton getInstance(){ return SingletonHolder.instance; } }
|
这种写法仍然使用JVM本身机制保证了线程安全问题;由于 SingletonHolder 是私有的,除了 getInstance() 之外没有办法访问它,因此它是懒汉式的;同时读取实例的时候不会进行同步,没有性能缺陷;也不依赖 JDK 版本。
枚举
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
| enum Dog { DogA(1, "dog1"),DogB(2,"dog2");
int age; String name;
Dog(int age, String name) { this.age = age; this.name = name; }
@Override public String toString() { return "Dog{" + "age=" + age + ", name='" + name + '\'' + '}'; } }
public static void main(String[] args) { Dog dog = Dog.DogA; System.out.println(dog);
}
|
总结
- 4种方式推荐使用后两种,其中枚举最适合,同时也最难理解。