设计模式之单例模式

单例模式属于创建型模式,创建型模式帮助我们创建对象,对象有简单有复杂的。

单例模式的概念十分简单:保证一个类只有一个实例(对象),并且提供一个开放的方法访问该实例(对象)。虽然概念简单,但是其应用场景确实十分的广泛。例如:

  • spring中,每个Bean默认是单例的。
  • 数据库连接池的设计也是采用的单例模式。
  • 在servlet编程中,每个servlet也是单例的。
  • ……

下面介绍5中常见的单例模式实现方式。

饿汉式


/**
 * 饿汉式单例模式
 * @author KKSJS
 *
 */
public class SingletonDemo01 {

    //提供一个私有的static成员变量
    private static SingletonDemo01 singletonDemo01 = new SingletonDemo01();
    //将构造器私有化
    private SingletonDemo01(){}
    //提供一个唯一的公共方法,将上面的变量返回给调用者
    public static SingletonDemo01 getInstance(){
        return singletonDemo01;
    }
}
public class TestSingleton {
    public static void main(String[] args) {
        System.out.println(
                SingletonDemo01.getInstance() == SingletonDemo01.getInstance());
    }
}

打印结果为true,说明单例模式创建成功。

在饿汉式的单例模式代码中,singletonDemo01这个变量会在类加载时初始化。虚拟机保证只加载一次这个类,所以不存在并发访问的问题。

因为singletonDemo01属性是静态的,所以会立即加载,当类加载器加载SingletonDemo01这个类的时候,就把这个对象new出来初始化给singletonDemo01变量,不管需不需要使用这个对象,都会new出来,所以称之为饿汉式。对于饿汉式,如果没有调用getInstance()方法,就会造成资源浪费,因为类的对象无论如何都会被创建。

懒汉式


/**
 * 懒汉式单例模式
 * @author KKSJS
 *
 */
public class SingletonDemo02 {

    private static SingletonDemo02 singletonDemo02;
    private SingletonDemo02(){}
    public static synchronized SingletonDemo02 getInstance(){
        if (singletonDemo02==null) {
            singletonDemo02 = new SingletonDemo02();
        }
        return singletonDemo02;
    }
}

这种单例模式称之为懒汉式。懒汉式实现了延迟加载,所谓的延迟加载指的是当我们调用getInstance()方法时才会创建对象,这样的话资源利用率得到的提高。但是随之而来的问题是调用效率变低了,因为我们在getInstance()方法上加上了synchronized关键字。

因为getInstance()这个方法有可能多个线程都在调用,所以要加一个synchronized关键字,避免在并发量高的时候创建多个对象的问题。那么如果不加这个关键字会出现什么问题呢?

假设某一个线程A刚执行完if判断,即被挂起。线程B进行if判断后,执行了s=new SingletonDemo01();随即B又被挂起,然后A又从被挂起的地方执行,又new了一个对象,这样就创建出来了两个对象。这与单例模式的初衷是相违背的。

静态内部类实现


/**
 * 静态内部类实现单例模式
 * @author KKSJS
 *
 */
public class SingletonDemo04 {

    private SingletonDemo04(){}
    private static class SingletonClassInstance{
        private static SingletonDemo04 singletonDemo04 = new SingletonDemo04();
    }
    public static SingletonDemo04 getInstance(){
        return SingletonClassInstance.singletonDemo04;
    }
}

这种方式由于外部类没有static属性,不会立即加载,所以资源利用率较高。只有真正调用getInstance()方法后才会初始化静态内部类,才会创建对象,所以实现了懒加载,即资源利用率高。

双重检测锁实现


/**
 * 双重检测锁式单例模式
 * @author KKSJS
 *
 */
public class SingletonDemo03 {

    private static SingletonDemo03 singletonDemo03 = null;
    private SingletonDemo03(){}
    public static SingletonDemo03 getInstance(){
        if(singletonDemo03 == null){
            SingletonDemo03 temp;
            synchronized (SingletonDemo03.class) {
                temp = singletonDemo03;
                if (temp == null) {
                    synchronized (SingletonDemo03.class) {
                        if (temp==null) {
                            temp = new SingletonDemo03();
                        }
                    }
                    singletonDemo03 = temp;
                }
            }
        }
        return singletonDemo03;
    }
}

这种方式比较复杂,不是很常用。这种模式将同步内容下放到if内部,不必每次获取对象时都同步,只有第一次才同步。由于JVM底层模型原因,这种方式偶尔会出问题,不推荐使用。

枚举方式实现


/**
 * 枚举实现单例模式
 * @author KKSJS
 *
 */
public enum SingletonDemo05 {

    //定义一个枚举元素,它表示一个SingletomDemo05类的对象
    singletonDemo05;
}

可以看到这种方式代码十分的简洁,由于枚举类天然具有单例属性,所以用来创建单例对象是最合适的。

public class TestSingleton {
    public static void main(String[] args) {
        System.out.println(
                SingletonDemo05.singletomDemo05==SingletonDemo05.singletomDemo05);
    }
}

经过测试,打印结果为true,说明这种方式是可行的,并且枚举方式是五种方式里面最简单的。但是这种方式唯一的问题就是没有实现懒加载,即无论我们使不适用,对象都会创建。

posted @ 2018-05-28 15:54  driveby  阅读(106)  评论(0编辑  收藏  举报