设计模式——单例模式

设计模式包括三大类

创建型模式(主要用来建立对象)

    单例模式 工厂模式 抽象工厂模式 建造者模式 原型模式

结构型模式

    适配器模式 桥接模式 装饰模式 组合模式 外观模式 享元模式 代理模式

行为型模式

    模板方法模式 命令模式 迭代器模式 观察者模式 中介者模式 备忘录模式 解释器模式  状态模式 策略模式 职责连模式 访问者模式

首先来说单例模式

核心作用:保证一个类只有一个实例,并且提供一个访问该实例的全局访问点

单例模式的常见应用场景:

Windows的任务管理器以及回收站

项目中,读取配置文件的类,一般也只有一个对象。没有必要每次使用配置文件数据,每次new一个对象去读取

网站的计数器,也是采用单例模式实现。否则难以同步。

应用程序的日志应用,一般都采用单例模式实现,这一般是由于共享的日志文件一直处于打开状态,因为只能有一个实例去操作,否则内容不好追加

数据库连接池的设计一般采用单例模式,因为数据库连接是一种数据库资源

操作系统的文件系统,也是大的单例模式实现的具体例子,一个操作系统只能有一个文件系统

Application也是典型的单例应用

Spring中每个bean默认是单例(singleton)的

在SpringMVC/Struts1框架中,控制器对象也是单例的

 

单例模式的优点:

由于单例模式只生成一个实例,减少了系统性能开支,当一个对象的产生需要比较多的资源时,如读取配置,产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后永久驻扎内存的方式来解决

单例模式可以在系统设置全局的访问点,优化和共享资源访问,例如可以设计一个单例类,负责所有数据表的映射处理

 

常见的五种单例模式及实现方式:

主要::

    饿汉式:(线程安全,效率高,但是,不能延时加载)

    懒汉式:(线程安全,效率相对不高,但是,可以延时加载)

其他:

    双重检测锁式(由于JVM底层内部模型原因,偶尔会出现问题,不建议使用)

    静态内部类式 (线程安全,效率高,可以延时加载)

    枚举单例(线程安全,效率高,不能延时加载)

 

饿汉式实现:

1.构造方法私有

2.定义一个私有静态变量,并通过私有构造方法创建对象

3.定义一个public的静态方法,返回静态变量的值

public class SingletonHungry {

    //构造方法私有化
    private SingletonHungry (){}

    //定义一个私有静态变量,获取私有构造方法 饿汉式 很饥饿 类初始化时立即加载对象(没有延时加载的优势)
    //类加载的时候 是线程安全的 如果没有使用该类 但已经在初始化的时候加载了 浪费资源 饿汉式的缺点
    private static SingletonHungry instance= new SingletonHungry ();

    //定义一个public方法,返回静态变量的值 方法没有同步效率高
    public static SingletonHungry getInstance(){

        return instance;
    }
}

饿汉式单例模式中,static变量会在类装载时初始化,此时也不会涉及多个线程对象访问该对象的问题。虚拟机保证只会装载一次该类,肯定不会发生并发访问的问题。因此可以省略synchronized关键字

类初始化的时候就会创建对象,如果未使用该对象 会造成资源的浪费 真实由于创建对象时机很快 所以叫饿汉式

调用效率很频繁的时候用饿汉式  类创建对象的成本很高的话用懒汉式

 

懒汉式(单例对象延迟加载)

实现:

1.构造方法私有化

2.定义一个私有静态变量

3.定义一个public的是static方法,在该方法被调用时再去创建对象并赋值给私有静态变量 并返回该私有变量

public class SingletonLazy {

    //构造方法私有化
    private SingletonLazy(){}

    //定义一个私有静态变量,不初始化这个对象(延时加载,用的时候在创建)
    private static SingletonLazy instance;
 
    //定义一个public方法,在该方法被调用时再去创建对象并赋值给私有静态变量 并返回该变量 调用时才创建对象,所以需要加同步
    public static synchronized SingletonLazy getInstance(){

        if(null == instance){
            instance= new SingletonLazy ();
        }
        return instance;
    }
}

2.定义

要点:延迟加载,在真正需要的时候才加载

缺点:资源利用率高了。但是,每次调用getInstance()方法都要同步,并发效率低

为了综合弥补懒汉和饿汉的缺陷,讲一下另外 一些单例模式

 

双重检测锁实现

这个模式将同步内容下放到if内部,提高了执行的效率,不必每次获取对象时都进行同步(对懒汉式缺点的优化),只在第一次访问的时候才同步,以后访问就不用进行同步

代码实现:

public class SingletonDoubbleKey {

    //构造方法私有化
    private SingletonDoubbleKey (){}
 
    //定义一个私有静态变量  volatile防止指令重排
    private static volatile SingletonDoubbleKey instance;
 
    public static  SingletonDoubbleKey getInstance(){

        if(null == instance){
            synchronized(SingletonDoubbleKey.class){//将synchronized放到if里
                if(null == instance){//双重检测
                    instance = new SingletonDoubbleKey();                    
                }
            }
           
        }

        return instance;
    }
}

由于编译器优化原因和JVM底层内部模型原因,偶尔会出现问题,不建议使用。所以了解即可

 

静态内部类实现方式(也是一种懒加载方式)

实现代码:

public class SingletonDemo4 {

    //构造方法私有化
    private SingletonDemo4(){}

    //定义私有静态内部类
    private static class Singleton{

        private static final SingletonDemo4 instance = new SingletonDemo4();
    }
 
    public static  SingletonDemo4 getInstance(){

        return Singleton.instance;
    }
}

要点:

外部类没有static属性,不会像饿汉式那样立即加载对象

只有真正调用getInstance()方法,才会加载静态内部类。加载类时是线程安全的。instance是static final类型,保证了内存中只有这样一个实例存在,而且只能被赋值一次,从而保证了线程安全

兼备了并发高效调用和延迟加载的优势

 

使用枚举实现单例模式

代码实现:

public enum SingletonDemo5 {

    //定义一个枚举的元素,它代表了一个singleton的一个实例
    INSTANCE;

    /**
    * 单例可以有自己的操作
    */
    public void operation(){
    //功能处理
    }
}

优点:

实现简单

枚举本身就是单例模式。由JVM从根本上提供保证,避免通过反射和反序列化的漏洞

缺点:

无延迟加载

 

总结:

单例模式实现

主要:

饿汉式(线程安全,调用效率高。但是,不能延迟加载)

懒汉式(线程安全,调用效率不高,同步方法需要等待,可以延迟加载)

其他:

双重检测锁式(由于JVM底层内部模型原因,偶尔会出现问题,不推荐使用)

静态内部类式(线程安全,调用效率高,而且可以延迟加载)

枚举式(线程安全,效率高,不可以延迟加载 可以天然的防止反射和反序列化漏洞)

 

如何选用

单例对象占用资源少,不需要延迟加载时

 枚举式好于饿汉式

单例对象占用资源大,需要延迟加载

静态内部类式好于懒汉式

 

原创不易:转载请标明出处:https://mp.youkuaiyun.com/postedit/80866450 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值