zoukankan      html  css  js  c++  java
  • 设计模式之单例模式

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

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

    • 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,说明这种方式是可行的,并且枚举方式是五种方式里面最简单的。但是这种方式唯一的问题就是没有实现懒加载,即无论我们使不适用,对象都会创建。

  • 相关阅读:
    IP地址查询接口
    Windows2008防火墙封ip
    UI设计原则
    iis7.5中使用fastcgi方式配置php5.6.5
    serv-u设置被动模式注意的问题
    mysql函数计算地表两点间距离
    vs2012出现无法启动iis express web 服务器的错误
    基于jquery的表单校验插件
    基于jquery的表单校验插件
    php redis 负载均衡[转]
  • 原文地址:https://www.cnblogs.com/KKSJS/p/9622821.html
Copyright © 2011-2022 走看看