• 单例模式(Singleton)


    单例模式(Singleton

      确保某个类只有一个实例,并且自行实例化并向整个系统提供这个单例。

    单例对象能保证在一个JVM中,该对象只有一个实例存在。好处如下:

      (1)某些类的创建比较频繁,对于一些大型的对象,系统开销很大;

      (2)省去了new操作符,降低了系统内存的使用频率,减轻GC压力;

    单例模式的使用场景

      在一个系统中,要求一个类有且仅有一个对象,如果出现多个对象就会出现问题,则可采用单例模式,具体的场景如下:

      (1)要求生产唯一序列号的环境;

      (2)在整个项目中需要有访问一个共享访问点或共享数据,例如一个Web页面上的计数器,可以不用每次刷新都纪录到数据库中,使用单例模式保持计数器的值,并确保线程的安全的。

      (3)创建一个对象需要的资源过多,如要访问IO、访问数据库等资源。

      (4)需要定义大量静态常量和静态方法(如工具类)的环境,可以采用单例模式

    饿汉式(单例实例在类装载的时候就创建)

    /**
     * 饿汉式,单例实例在类装载的时候就创建
     *
     */
    public class Singleton1 {
        //构造方法私有
        private Singleton1() {        
        }
        //在类装载时创建实例
        private static Singleton1 instance = new Singleton1();
        //获得实例
        public Singleton1 getInstance(){
            return instance;
        }
    }

    懒汉式(单例实例在第一次被使用时构建,延迟初始化。)

    /**
     * 懒汉式,单例实例在第一次使用时再创建,延迟初始化
     *
     */
    public class Singleton2 {
        //持有私有静态实例,防止被引用,赋值为null,实现延迟加载
        private static Singleton2 instance = null;
        //构造方法私有,防止被实例化
        private Singleton2() {        
        }
        //创建实例
        public static Singleton2 getInstance(){
            if (instance == null) {
                instance = new Singleton2();
            }
            return instance;
        }
    }

      这个类基本可以满足要求,但是在多线程的环境下,会出现问题,解决办法:对getInstance()方法加synchronized关键字。但是synchronized关键字锁住的是这个对象,会降低性能,每次调用getInstance()方法都要对对象上锁,只有在第一次创建对象的时候需要加锁,之后就不需要了。

    instance = new Singleton();是分两步来执行的。JVM不保证这两个操作的先后顺序,可能JVM会为新的Singleton实例分配空间,直接赋值给instance成员,然后再初始化这个Singleton实例,这样就可能出错了。

      要想实现线程安全,可以采用静态初始化器的方式,它可以由JVM来保证线程的安全性。但是这样会浪费一定的空间,这种实现方式会在类加载的时候就初始化对象,不管需不需要。

      类级内部类(静态内部类),加载外部类的时候,并不会同时加载其静态内部类,只有在发生调用的时候才会进行加载,加载的时候就会创建单例实例并返回,实现了延迟加载;至于同步问题,采用静态初始化器的方式。

    实际情况是,单例模式使用内部类来维护单例的实现,JVM内部的机制能够保证当一个类被加载时,这个类的加载过程是线程互斥的。当我们第一次调用getInstance()的时候,JVM能够帮我们保证instance只被创建一次,会保证把赋值给instance的内存初始化完毕。

    /**
     * 使用内部类维护单例的实现
     *
     */
    public class Singleton3 {
        //构造方法私有,防止被实例化
        private Singleton3() {        
        }
        //使用内部类来维护实例
        private static class SingletonFactory{
            private static Singleton3 instance = new Singleton3();
        }
        //获取实例
        public Singleton3 getInstance(){
            return SingletonFactory.instance;
        }
        //如果对象被用于实例化,可以保证对象在序列化前后保持一致
        public Object readReaolve(){
            return getInstance();
        }
    }

    类的静态方法和单例的区别

      (1)静态类不能实现接口。(接口中不允许有static修饰的方法)

      (2)单例可以被延迟初始化,静态类一般在第一次加载时初始化。之所以延迟加载,是因为有些类比较庞大,延迟加载有助于提升性能。

      (3)单例类可以被继承,他的方法可以被重写,但是静态类内部方法都是static,无法被重写(只有非静态方法才有多态)。

      在Spring中,每个Bean都是默认单例的,优点是Spring容器可以管理这些Bean的生命期,决定什么时候创建出来,什么时候销毁,销毁的时候要如何处理等。

    参考博客

    [1] 单例模式

    http://www.cnblogs.com/cbf4life/archive/2009/12/18/1626850.html

    [2] Java开发中的23种设计模式详解(转)

    http://www.cnblogs.com/maowang1991/archive/2013/04/15/3023236.html

  • 相关阅读:
    mybatis 枚举的支持
    windows系统下Disconf web安装-分布式配置管理平台
    Tomcat启动报Error listenerStart错误
    Java并发编程:volatile关键字解析
    深入理解java异常处理机制
    Java 常见异常种类
    Java RMI与RPC的区别
    Java的快速失败和安全失败
    mysql数据类型介绍(含text,longtext,mediumtext说明)
    DTO – 服务实现中的核心数据
  • 原文地址:https://www.cnblogs.com/ghq120/p/8435339.html
Copyright © 2020-2023  润新知