单例模式

123 阅读2分钟

单例模式一般在全局只能出现一个实例时使用,比如系统配置文件、注册表等对象,这些对象维护着系统运行所需的基本属性;系统中不应该出现多个对象维护,这样就会出现多份配置文件,程序不知道该使用那一份,从而使系统发生一些奇怪的问题。

传统的单例模式设计时有以下几点注意事项:

  • 构造方法私有
  • 类持有自身的一个静态实例
  • 提供一个获取实例的方法

代码如下:

public class SingleDemo {
    private static SingleDemo singleDemoInit;
    private SingleDemo() {
    }
    
    public SingleDemo getInstence() {
        if (null == singleDemoInit) {
            singleDemoInit = new SingleDemo();
        }
        return singleDemoInit;
    }
}

这样在单线程下是完全OK的,但是在多线程情况下就不一定了

如图中所写,如果改该对象保存了一些参数,就会丢失数据,对此有三种解决办法:

a、使用 synchronized关键字

public class SingleDemo {
    private static SingleDemo singleDemoInit;
    private SingleDemo() {
    }

    public synchronized SingleDemo getInstence() {
        if (null == singleDemoInit) {
            singleDemoInit = new SingleDemo();
        }
        return singleDemoInit;
    }
}

但是,众所周知,使用同步时就会出现效率问题,这就需要根据系统的具体情况决定,如果对加同步字段的方法使用不多或者可以接受这种方式降低的效率,那么你就可以采用该方式。

b、使用饿汉模式 即在声明静态实例时直接创建,每次返回该实例

public class SingleDemo {
    private static SingleDemo singleDemoInit = new SingleDemo();
    private SingleDemo() {
    }

    public  SingleDemo getInstence() {
        return singleDemoInit;
    }
}

但是这种方式会在系统启动时就由jvm创建,需要注意的时如果你的系统存在多个类加载器,那么他也可能出现多个实例,所以需要指定该类的类加载器。

c、双重检查机制 对创建的代码执行两次检查,并在第二次检查时使用该类的类对象进行加锁

public class SingleDemo {
    private volatile static SingleDemo singleDemoInit;
    private SingleDemo() {
    }
    public  SingleDemo getInstence() {
        if (null == singleDemoInit) {
            synchronized (SingleDemo.class) {
                if (null == singleDemoInit) {
                    singleDemoInit = new SingleDemo();
                }
            }
        }
        return singleDemoInit;
    }
}

只有在第一次创建时才会进入同步方法,所以即使同步也不会有效率损耗。

以上就是观看Head First之后自己总结的