博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java中如何实现单例模式
阅读量:7055 次
发布时间:2019-06-28

本文共 2094 字,大约阅读时间需要 6 分钟。

Java中,单例模式通常有2种分类饿汉模式和懒汉模式。

饿汉模式指的是单例实例在类装载时就被创建了。

懒汉方式值的是单例实例在首次使用时才被创建。

无论是饿汉模式还是懒汉模式,都是用了一个静态成员变量来存放真正的实例。并且私有化构造函数,防止被外部实例化。

单例(饿汉模式)代码:

public class Singleton {    private final static Singleton INSTANCE = new Singleton();    //私有化构造方法,防止被实例化       private Singleton() {    }    public static Singleton getInstance() {        return INSTANCE;    }}

单例懒汉模式代码,注意静态字段声明的时候,有一个volatile关键字,并且代码中两次判断是否是null,这种双重检测的机制为了应对多线程环境。

public class Singleton {    private static volatile Singleton INSTANCE = null;    //私有化构造方法,防止被实例化    private Singleton() {    }    //双重检测    public static Singleton getInstance() {        if (INSTANCE == null) { //①            synchronized (Singleton.class) { //②                if (INSTANCE == null) { //③                    INSTANCE = new Singleton(); //④                }            }        }        return INSTANCE;    }}

需要注意的是,即使这种Doublecheck在C++中有效,但对JAVA5之前的代码还是有一点问题的。

原因在于,编译器会优化代码,可能导致赋值语句乱序执行,上述代码中,如果有2个线程A,B。A线程已经进入4位置,当4位置的代码执行时,需要注意 INSTANCE = new Singleton()这个行代码不是一个原子操作。

JVM可能在完成Singleton类的构造方法之前,会先把一块还未初始化完成的内存地址先分配给INSTANCE,而此时如果线程B进入1位置,会认为INSTANCE已经存在,从而返回了一个未初始化完成的内存块,这可能导致程序崩溃。正是由于是先给INSTANCE赋值在初始化内存块,还是先初始化内存块再复制给INSTANCE,这个顺序无法保证,所以这种机制会出现问。所以在JAVA5之后,扩充了 volatile关键字,确保一个变量写入和读取操作的顺其不会被编译器优化成乱序,volatile变量也不会被缓存到cpu寄存器中,保证了其读取的一致性。

这和C#中的volatile的关键字是一样的作用。

除了上面2中常见的方法之外,还有其他方法。比如下面一种比较经典的实现方法,使用一个内部类,JVM自身保证了自身安全,这个模式也是在《Effective Java》这本书中推荐的,这种方式不依赖于java版本。

public class Singleton {    private Singleton() {    }    public static final Singleton getInstance() {        return InnerClass.INSTANCE;    }    private static class InnerClass {        private static Singleton INSTANCE = new Singleton();    }}

但在JAVA5之后,最简单的单例实现方法是使用enum类型。

enum关键字是JAVA5中新增的,它和class,interface一样,也是一种数据类型。可以把它看成是一种特殊的类。可以在enum内部实现构造方法,字段,方法等,还可以实现接口。不过也有一些限定,枚举类中的构造器,默认为private修饰,且只能使用private。枚举类的所有实例必须在类中的第一行显式列出,否则这个枚举类不可能产生实例。JVM保证了这个每个枚举值只被初始化一次。正是由于这样一个特点,我们可以用如下代码可以实现单例。

public enum Singleton {INSTANCE;    public void dosth(String arg) {        // 逻辑代码    }}

上述单例中,Singleton.INSTANCE就表示了一个单例。非常简单高效。

注意Java中enum关键字和C#中的enum,差别很大,C#中不能使用这种方式。

转载地址:http://lsool.baihongyu.com/

你可能感兴趣的文章
阿里云服务器怎么重装系统?
查看>>
锌财经3月科技主题沙龙丨袋鼠云-云掣CEO徐进挺(丁原)受邀探讨“未来智能时代下的行业生存机会” ...
查看>>
【镜像更新】Windows Server 2012 R2 数据中心版
查看>>
日志服务Python消费组实战(三):实时跨域监测多日志库数据 ...
查看>>
网站被黑跳转到其他网站的解决办法
查看>>
填报脚本之轻松搞定复杂表的数据入库
查看>>
HttpClient在多线程环境下踩坑总结
查看>>
接入高防后为什么有一些网站,APP等会出现延迟,打开速度慢等问题? ...
查看>>
Vue-cli3 简qian易yi教程
查看>>
Linux服务器---DansGuardian
查看>>
Intel处理器供应紧张最晚4季度缓解,俄勒冈州新工厂6月底前开建 ...
查看>>
Confluence 6 计划你的升级
查看>>
网站常见问题1分钟定位 - 如何使用阿里云ARMS轻松重现用户浏览器问题 ...
查看>>
【机器学习PAI实战】—— 玩转人工智能之综述
查看>>
基于HBase和Spark构建企业级数据处理平台
查看>>
MyBatis的配置方式
查看>>
5分钟,关于Python 解包,你需要知道的一切
查看>>
实验吧--隐写术
查看>>
NLP领域中更有效的迁移学习方法
查看>>
【全面解禁!真正的Expression Blend实战开发技巧】第六章 认识ListBox
查看>>