一天一个设计模式:单例模式

概念:

作为对象的创建模式,单例模式确保某一个类只有一个实例,而且自行实例化,并向整个系统提供这个实例。

特点:

  1.单例类只能有一个实例

  2.单例类必须创建自己的唯一实例

  3.单例类必须给其他所有对象提供这一实例。

饿汉式单例类

public class EagerSingleton {
    private static EagerSingleton instance = new EagerSingleton();
    /**
     * 私有默认构造子
     */
    private EagerSingleton(){}
    /**
     * 静态工厂方法
     */
    public static EagerSingleton getInstance(){
        return instance;
    }
}

由于选用的是静态资源,在类加载的时候,静态变量instance就会被初始化,类的唯一变量也在这时候创建了出来。

饿汉式,顾名思义,急于创建对象,在类加载的时候就完成了对象的创建。

饿汉式是一种采用“空间换取时间”,当类装载的时候就会创建类的实例,不管你用不用,先创建,调用的时候,无需判断直接使用,节约了时间。

懒汉式单例

public class LazySingleton {
    private static LazySingleton instance = null;
    /**
     * 私有默认构造子
     */
    private LazySingleton(){}
    /**
     * 静态工厂方法
     */
    public static synchronized LazySingleton getInstance(){
        if(instance == null){
            instance = new LazySingleton();
        }
        return instance;
    }
}

只有在真正需要的时候才会去创建,采用的是时间换空间,由于是线程安全的,会降低访问速度。固可使用双检锁的方式进行优化。

双重检查加锁

机制:并不是每次进入getInstance方法都需要同步,而不是先同步,进入方法后,先检查实例是否存在,如果不存在才进行下面的同步块,这是第一检查,进入同步块后,在检查实例是否存在,如果不存在,就在同步的情况下创建一个实例,这是第二重检查,这样一来就只需要同步一次,减少了多次在同步情况下进行判断所浪费的时间。

双检锁的关键是,使用volatile,它在此处的作用是,该变量将不会被某一个线程缓存,而是在共享内存中,被所有进行读写的内存共享到,从而保证多个线程能够正确处理该变量。

public class Singleton {
    private volatile static Singleton instance = null;
    private Singleton(){}
    public static Singleton getInstance(){
        //先检查实例是否存在,如果不存在才进入下面的同步块
        if(instance == null){
            //同步块,线程安全的创建实例
            synchronized (Singleton.class) {
                //再次检查实例是否存在,如果不存在才真正的创建实例
                if(instance == null){
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

注:由于volatile关键字会屏蔽虚拟机的一些代码优化,所以运行效率并不高,所有其实应该尽量避免使用双检锁的方式来实现单例。

懒加载模式(内部类形式)

什么是内部类:

  内部类就是指在类里面的类,被static修饰的内部类,称为类级内部类,无static修饰的内部类称为对象级内部类。

  类级内部类是外部类的static部分,它的对象与外部类对象间并没有依赖关系,因此可以直接创建。而对象级内部类的实例,是绑定在外部对象的实例中的。

  类级内部类中,可以定义静态方法,在静态方法中只能够引用外部类中的静态成员或者成员变量。

  类级内部类相当于外部类的成员,只有在第一次被使用的时候才会加载。

多线程缺省同步锁的相关知识:

  在下面操作下,jvm会自动为该操作进行同步,以避免出现并发安全问题。

  1.由静态初始化器(在静态字段上或static{}块中的初始化器)初始化数据。

  2.访问final字段时,

  3.在创建线程前创建对象时,

  4.线程可以看见它将要处理的对象时。

综上的解决思路:

  采用静态初始化器的方式通过jvm来保证线程的安全性,采用类级内部类的方式实现延迟加载。

public class Singleton {
    
    private Singleton(){}
    /**
     *    类级的内部类,也就是静态的成员式内部类,该内部类的实例与外部类的实例
     *    没有绑定关系,而且只有被调用到时才会装载,从而实现了延迟加载。
     */
    private static class SingletonHolder{
        /**
         * 静态初始化器,由JVM来保证线程安全
         */
        private static Singleton instance = new Singleton();
    }
    
    public static Singleton getInstance(){
        return SingletonHolder.instance;
    }
}

  在getInstance第一次被调用时,会读取内部类的instance,这时,类级内部类完成初始化,从而创建单例,实现了延时加载,由于是静态成员,只有在类加载的时候才加载一次,且通过jvm的缺省方式实现了线程安全问题。

单例类枚举:

  单元素的枚举类型已经成为实现Singleton的最佳方法。用枚举来实现单例非常简单,只需要编写一个包含单个元素的枚举类型即可。

public enum Singleton {
    /**
     * 定义一个枚举的元素,它就代表了Singleton的一个实例。
     */
    
    uniqueInstance;
    
    /**
     * 单例可以有自己的操作
     */
    public void singletonOperation(){
        //功能处理
    }
}

  使用枚举来实现单实例控制会更加简洁,而且无偿地提供了序列化机制,并由JVM从根本上提供保障,绝对防止多次实例化,是更简洁、高效、安全的实现单例的方式。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Linux驱动

22.QT-QXmlStreamReader解析,QXmlStreamWriter写入

XML 用于存储数据,数据的形式类似于树结构(参考: http://www.runoob.com/xml/)

17550
来自专栏JetpropelledSnake

SQL学习笔记之简易ORM

1 、我在实例化一个user对象的时候,可以user=User(name='lqz',password='123')

6110
来自专栏我是攻城师

理解Java里面的序列化和反序列化

38040
来自专栏IT可乐

Java IO详解(一)------File 类

File 类:文件和目录路径名的抽象表示。 注意:File 类只能操作文件的属性,文件的内容是不能操作的。 1、File 类的字段 ?   我们知道,各个平台之...

23090
来自专栏小二的折腾日记

day5(面向对象2)

用来将文件或文件夹封装成对象。 方便对文件与文件夹的属性信息进行操作。 File对象可以作为参数传递给

6910
来自专栏xingoo, 一个梦想做发明家的程序员

JSP 内置对象

  本篇继前两篇内置对象,继续记录JSP中的其他的内置对象:application,page,pageContext,config,exception   ap...

21870
来自专栏python爬虫日记

转载:python的编码处理(一)

最近业务中需要用 Python 写一些脚本。尽管脚本的交互只是命令行 + 日志输出,但是为了让界面友好些,我还是决定用中文输出日志信息。 

11420
来自专栏ml

strcpy和memcpy的区别

strcpy和memcpy都是标准C库函数,它们有下面的特点。 strcpy提供了字符串的复制。即strcpy只用于字符串复制,并且它不仅复制字符串内容之外,还...

36260
来自专栏安恒信息

JoSQL内存数据库远程代码执行漏洞(含EXP)

JoSQL全称SQL for Java Objects,提供了应用SQL语句的Java对象的集合的能力开发,JoSQL提供了搜索,排序,group等对Java对...

393110
来自专栏JAVA高级架构

Java设计模式--单例模式

23840

扫码关注云+社区

领取腾讯云代金券