定义
确保一个类只有一个实例, 并提供一个全局访问点.
通俗点讲单例模式就是保证只 new 了一个对象每次获取都是这一个对象的模式.
最简单的单例实现
- public class Singleton {
- // 使用一个私有的类成员保存唯一的实例对象.
- private static Singleton uniqueInstance;
- // 私有化构造方法, 保证不能从外部实例化.
- private Singleton() {}
- // 提供一个公有的静态方法获取到唯一的实例.
- public static Singleton getInstance() {
- // 判断唯一实例是否被实例化了
- if(uniqueInstance==null)
- // 未实例化则实例化
- uniqueInstance = new Singleton();
- // 返回唯一实例.
- return uniqueInstance;
- }
- }
但是这种方式在多线程的情况下不能保证只有一个实例, 因为在判断对象为空时可能有多个线程通过并实例化对象. 如下
- public class Main {
- public static void main(String[] args) {
- // TODO Auto-generated method stub
- for(int i = 1 ; i < 100 ;i++) {
- new Thread(new Runnable() {
- @Override
- public void run() {
- Singleton instance = Singleton.getInstance();
- System.out.println(instance.hashCode());
- }}).start();
- }
- }
- }
输出
- 350646954
- 2006299335
- 2006299335
- 2006299335
- 350646954
- 2006299335
- 2006299335
- 2006299335
- 2006299335
- 2006299335
- 2006299335
- ***
更加安全的单例模式
- // 双重监测加锁单例
- public class DoubleCheckSingleton {
- // 静态变量保存唯一实例, 使用 volatile 关键字保证线程可见性.
- private static volatile DoubleCheckSingleton uniqueInstance;
- // 私有化构造函数
- private DoubleCheckSingleton() {}
- // 获取实例的方法
- public static DoubleCheckSingleton getInstance() {
- // 判断唯一实例是否实例化, 避免不必要的加锁带来的性能问题.
- if(uniqueInstance==null) {
- // 加锁保证判断和实例化操作的原子性
- synchronized(DoubleCheckSingleton.class) {
- // 再次判断是否为空, 因为可能多个线程通过上面的第一次检测.
- if(uniqueInstance==null)
- uniqueInstance = new DoubleCheckSingleton();
- }
- }
- return uniqueInstance;
- }
- }
- // 饿汉模式单例
- public class EagerSingleton {
- // 静态变量保存唯一实例, 保证在类加载时实例化一次.
- private static EagerSingleton uniqueInstance = new EagerSingleton();
- // 私有化构造函数
- private EagerSingleton() {
- }
- // 获取实例方法
- public static EagerSingleton getInstance() {
- return uniqueInstance;
- }
- }
- // 静态内部类单例
- public class StaticInnerClassSingleton {
- // 私有化构造函数
- private StaticInnerClassSingleton() {}
- // 定义一个静态内部类, 在该类被加载时实例化一个唯一的实例并保存在变量中.
- static class InstanceHolder{
- private static StaticInnerClassSingleton uniqueInstance = new StaticInnerClassSingleton();
- }
- // 获取唯一实例
- public StaticInnerClassSingleton getInstance() {
- return InstanceHolder.uniqueInstance;
- }
- }
恶汉模式和静态内部类模式的区别在于是否在类加载时就实例化对象. 为了验证我们在它们的构造方法中打印一句话.
- private EagerSingleton() {
- System.out.println("EagerSingleton init");
- }
- private StaticInnerClassSingleton() {
- System.out.println("StaticInnerClassSingleton init");
- }
测试
- public class Main {
- public static void main(String[] args) throws ClassNotFoundException {
- // TODO Auto-generated method stub
- Class.forName("singleton.EagerSingleton");
- Class.forName("singleton.StaticInnerClassSingleton");
- }
- }
结果
EagerSingleton init
总结
单例模式是一个很实用的模式, 虽然只需要一个类就可以实现该模式, 但是我们需要注意在多线程环境下它的安全性.
源代码: https://github.com/Panici4/DesignPattern/tree/master/singleton
来源: http://www.bubuko.com/infodetail-2743125.html