Java单例模式的5种实现方法

Java单例模式的5种实现方法单例模式有5种实现方式:饿汉、懒汉、双重校验锁、静态内部类和枚举饿汉类加载的时候就创建了实例优点:类加载的时候创建一次实例,避免了多线程同步问题缺点:即使单例没被用到也会创建,浪费内存publicclassSingleton{privatestaticSingletoninstance=newSingleton();privateSing…

大家好,又见面了,我是你们的朋友全栈君。

单例模式有5种实现方式:饿汉、懒汉、双重校验锁、静态内部类和枚举

饿汉

类加载的时候就创建了实例
优点:类加载的时候创建一次实例,避免了多线程同步问题

缺点:即使单例没被用到也会创建,浪费内存

public class Singleton {
    private static Singleton instance = new Singleton();
    private Singleton() { }
    public static Singleton getInstance() {
        return instance; 
    }
}

饿汉-变种

public class Singleton {
    private static Singleton instance = null;
    static {
        instance = new Singleton();
    }
    private Singleton() { }
    public static Singleton getInstance() {
        return this.instance; 
    }
}

懒汉-(非线程安全)

优点:需要时才去创建
缺点:没有考虑线程安全问题,多个线程并发调用getInstance,可能会创建多个实例

public class Singleton {
    private static Singleton instance = null;
    private Singleton() { }
    public static Singleton getInstance() {
          if (instance == null) {
                instance = new Singleton();
          }
        return instance;
    }
}

懒汉-(线程安全)

缺点:性能问题,添加了synchronized的函数比一般方法慢得多,若多次调用getInstance,则累积的性能损耗特别大。

public class Singleton {
    private static Singleton instance = null;
    private Singleton() { }
    public static Synchronized Singleton getInstance() {
          if (instance == null) {
                instance = new Singleton();
          }
        return instance;
    }
}

考虑到以上的性能问题,所以又有一种双重校验锁的实现方式:

双重校验锁

大部分情况下,同步代码块都不会执行到,提高了程序的性能。

有一种情况,两个线程ThreadA,ThreadB,如果threadA执行到了第一个if条件判断,instance = null;ThreadB也执行到了if条件判断instance = null,所以A和B会依次执行同步代码块里的代码。为了避免创建两个实例,因此又在同步代码块里添加了if条件进行二重检验。

public class Singleton {
    private static Singleton instance = null;
    private Singleton() { }
    public static Singleton getInstance() {
        if (instance == null) {
            synchronized(Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

隐患

1、此处涉及Java的指令重排优化。指令重排优化是指在不改变原语义的情况下,通过调整指令的执行顺序让程序运行地更快。

2、JVM中没有规定编译器优化的相关内容,也即JVM可以自由地进行指令重排序的优化。

3、此问题的关键在于由于指令重排序优化的存在,导致初始化Singleton和将对象地址赋给instance字段的顺序是不确定的。

4、在某个线程创建单例对象时,在构造函数被调用前,就为该对象分配了内存空间并将对象的字段设置为默认值。此时就可以将分配的内存地址赋值给instance字段了,然而该对象可能还没初始化。若紧接着另一个线程来调用getInstance,获取到的就是状态不正确的对象,程序出错。

JDK5的修正:以上是双重校验锁失效的原因,不过在JDK1.5之后的版本添加了volatile关键字。

1、volatile的一个语义是禁止指令重排序优化,也就保证了instance变量被赋值的时候对象已经是初始化过的,从而避免了上述问题。

2、Java中的volatile变量是什么?

    (1)关键字的作用有两个:

          ①多线程主要围绕可见性和原子性两个特性展开,使用volatile关键字修饰的变量,保证了其在多线程之间的可见性,即每次读取到的volatile变量,一定是最新的数据。

         ②代码底层执行的顺序是Java代码–>字节码–>根据字节码执行对应的C/C++代码–>C/C++代码被编译成汇编语言–>和硬件电路交互。实际中,为了获取更好的性能,JVM可能会对指令进行重排序,多线程下可能会出现一些意想不到的问题。使用volatile则会禁止语义重排序,也一定程度上降低了代码执行效率。实践角度而言,volatile的一个重要作用就是和CAS结合,保证了原子性。

   (2)volatile是一个特殊的修饰符,只有成员变量才能使用它。在Java并发程序缺少同步类的情况下,多线程对成员变量的操作对其他线程是透明的。volatile变量可以保证下一个读取操作会在前一个写操作之后发生。

代码如下:

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; 
    }
}

 静态内部类

public class StaticSingleton {
    private StaticSingleton() {}
    private static class SingletonHolder {
        private static StaticSingleton INSTANCE = new StaticSingleton();
    }
    
    public static StaticSingleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}

枚举

public enum Singleton {
    INSTANCE;
    public void xx(){
    }
}

既能避免多线程同步问题,又能防止反序列化重新创建新的对象。

 

参考链接:

https://blog.csdn.net/fly910905/article/details/79286680

http://www.blogjava.net/kenzhh/archive/2016/03/28/357824.html

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/156818.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 如何用python刷屏(如何用python打开qq)

    python实现QQ和微信刷屏看过一些用来刷屏的程序,要么就只能刷屏QQ,要么就只能刷屏微信,今天博主就来把它一起实现了,而且用法超简单的哦!!!,希望可以帮助到你!废话不多说,先上代码,然后再进行详细介绍!!!frompynputimportmouse,keyboardfromtkinterimport*importtkinter.filedialogimporttimeroot=Tk()root.title(“信息刷屏”)root.geometry(“550×200

    2022年4月10日
    52
  • vmware虚拟机(四)之安装centos7图形界面Linux

    vmware虚拟机(四)之安装centos7图形界面Linux1.创建新的虚拟机2.根据提示一步一步操作,这里可以选择推荐的,也可以选择自定义3.虚拟机硬件兼容性一般选择默认的,即次虚拟机支持的最新版本,下面是一些兼容产品和限制,此虚拟机可安装的最大内存为64GB,处理器数目最多为16个。。。。4.为了练习安装Linux操作系统,我们选择稍后安装操作系统5.根据自己需要的操作系统版本选择,这里我们选择centos64位的Linux6.虚…

    2022年5月27日
    35
  • navicat在线生成激活码【最新永久激活】

    (navicat在线生成激活码)最近有小伙伴私信我,问我这边有没有免费的intellijIdea的激活码,然后我将全栈君台教程分享给他了。激活成功之后他一直表示感谢,哈哈~https://javaforall.net/100143.htmlIntelliJ2021最新激活注册码,破解教程可免费永久激活,亲测有效,上面是详细链接哦~83PV…

    2022年3月27日
    324
  • 去除a标签的下划线

    去除a标签的下划线很久没写忘记的差不多了,写下这个方便以后查看,好了话不多说,消除A标签的这么实现呢使用”text-decoration:none;”属性即可:1,一开始是这个样的,是不是有下划线2,下面是我添加了样式的<astyle=”text-decoration:none;”>我们没有下划线</a>结果如下,这样就没有下划线了…

    2022年6月2日
    37
  • (教程)手把手教你如何申请软件著作权

    (教程)手把手教你如何申请软件著作权「软件」通常指含有图形化界面的程序,但日常「科研程序」很少用得到图形界面,所以本文主要叙述科研程序(代码)的申请方式。专利保护的是思想,而软件著作权保护的是具体实现方式,因而软著的申请难度和价值都比专利要低不少。如果你是独立实现了已有的算法、独立复现了别人的程序,同样可以用来申请软件著作权。版权保护中心官网如下,申请软著均在此网站内完成:中国版权保护中心如果想省事的也可以考虑第三方服务申请我推荐使用:阿里云软件著作权申请​使用阿里云软件著作权申请有很多好处,阿里云为了拓展业务,费用和自己申请都是

    2022年9月22日
    0
  • pycharm系统找不到指定路径_win7找不到指定程序

    pycharm系统找不到指定路径_win7找不到指定程序对于许多程序初学者来说可能会碰到许多问题,首先可能在编译器运行程序时就会出现各种问题。我就是这样的一个程序员小白。在初次使用PyCharm编译器运行Python程序时就出现了系统找不到指定文件所在的位置的问题。在上网看了许多人提供的解决方案之后,我从中浏览几个与所出现问题相近的答案,并结合本人运行程序时出现的问题将解决方法作为学习笔记记录在此。希望对我今后的学习以及对于那些像我一样的初学者能够提供一些帮助。使用PyCharm运行程序时出现系统找不到指定文件位置的错误的解决:第一步:看到错误提示【

    2022年8月26日
    12

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号