单例模式,顾名思义,就是在Java程序中只有唯一一个实例,这样做的好处是可以在不需要多个实例的对象采用单例模式可以节省内存,否则会造成不必要的内存浪费。单例模式的定义为:保证一个类只有一个实例,自己可以初始化自己,且全局可以访问。该模式在Java中广泛使用,例如连接池,连接池一般只需要一个,就采用这种设计模式。

  单例模式又分为“饿汉”和“懒汉”两种。

  “饿汉”模式:

  “懒汉”模式:在懒汉模式情况下需注意并发情况下获取实例方法的线程安全问题,该问题指的是初始化多个实例,这样就违背了我们的初衷,虽然最后不可达的对象会被JVM回收,但是也存在隐患的问题。如下图,这里使用了synchonized关键字对该方法进行加锁,但是这种方式存在很明显的问题,接着往下看:

  我们使用synchonized是为了避免初始化多个实例,但是这个问题只存在于第一次访问该对象的时候,之后初始化好了 null != instance 了,就不存在这个问题了,但是由于synchonized关键字的存在,导致每次获取实例都会进行线程等待,很大程度上会影响执行的效率,有一种方式叫做双重检查加锁,如:

  

  现进行判断instance引用是否已经指向了内存空间(即判断instance ==null是否成立的条件),没有就对该对象进行加锁,然后在进行判断一次,还没有的话进行初始化,这样只有第一次初始化的时候会进行加锁校验,这就解决问题了吗?答案是并没有,请注意本段中加粗斜体的文字,由于JMM(Java Memory Model Java内存模型)中说了,存在三个特性,原子性,可见性,有序性,关于JMM会在之后自己深入学习的时候单独进行记录,现在不敢误导别人,读者只需先知道存在这个即可,java代码在进行编译的时候,会对代码指令进行重新排序,上述代码中我用红色箭头指向的那一句,并不是一个原子性操作(可理解为一步就能搞定的操作),其指令可以分解为:

  1.new 关键字在堆中为其分配内存空间

  2.在分配好的内存空间中初始化该对象

  3.将instance的引用指向该内存地址

  如果是顺序执行的话,自然没有什么问题,但是如果指令重新排序成为1,3,2这样来进行执行的话,在两个线程并发的情况下可能会导致这种情况:

  t1:发现instance引用未指向内存空间,抢占到锁,分配内存空间,将instance引用指向该空间,此时让出时间片

  t2:发现instance引用已经指向了某一内存空间,不进行初始化,直接引用该对象,但是请注意,此时该对象尚未进行初始化。

  这样就会导致问题产生,如何来解决呢?

  Java给我们提供了volatile关键字,被该关键字修饰的变量会依据“do-before”原则,所有该变量的读操作都会在写操作完成之后,禁止指令的重排序,这样就能保证t2线程拿到的是初始化好的对象了,所以,懒汉模式的最终代码如下:

  

  此外,还有一种使用静态内部类的方法通过饿汉模式中的类加载机制实现延迟加载和保证并发环境下的线程安全,只有在调用getInstance方法的时候才会加载内部类使用类加载机制进行初始化,天生线程安全:

  

  

单例模式详解以及需要注意的地方(Singleton)的更多相关文章

  1. 9种Java单例模式详解(推荐)

    单例模式的特点 一个类只允许产生一个实例化对象. 单例类构造方法私有化,不允许外部创建对象. 单例类向外提供静态方法,调用方法返回内部创建的实例化对象.  懒汉式(线程不安全) 其主要表现在单例类在外 ...

  2. JAVA设计模式(6:单例模式详解)

    单例模式作为一种创建型模式,在日常开发中用处极广,我们先来看一一段代码: // 构造函数 protected Calendar(TimeZone var1, Locale var2) { this.l ...

  3. Java设计模式之单例模式详解

    在Java开发过程中,很多场景下都会碰到或要用到单例模式,在设计模式里也是经常作为指导学习的热门模式之一,相信每位开发同事都用到过.我们总是沿着前辈的足迹去做设定好的思路,往往没去探究为何这么做,所以 ...

  4. Java双重校验单例模式详解

    单例模式双重检测java实现: public class Singleton { private volatile static Singleton instance = null; //#1 pub ...

  5. Java 单例模式详解

    概念: java中单例模式是一种常见的设计模式,单例模式分三种:懒汉式单例.饿汉式单例.登记式单例三种. 单例模式有一下特点: 1.单例类只能有一个实例. 2.单例类必须自己自己创建自己的唯一实例. ...

  6. C#单例模式详解

    C#要实现单例模式必须要有以下三点: 声明私有静态成员.私有化构造函数.静态函数返回实例. private static GameManager s_GameManager=null; private ...

  7. java单例模式详解

    饿汉法 饿汉法就是在第一次引用该类的时候就创建对象实例,而不管实际是否需要创建.代码如下: public class Singleton { private static Singleton = ne ...

  8. 【JAVA单例模式详解】

    设计模式是一种思想,适合于任何一门面向对象的语言.共有23种设计模式. 单例设计模式所解决的问题就是:保证类的对象在内存中唯一. 举例: A.B类都想要操作配置文件信息Config.java,所以在方 ...

  9. Objective-c单例模式详解

    转载自:http://www.jianshu.com/p/85618bcd4fee 单例模式出现以后,关于它的争执就一直存在.在开发项目中,有很多时候我们需要一个全局的对象,而且要保证全局有且仅有一份 ...

随机推荐

  1. macos下如何解决无法写ntfs格式的u盘或硬盘?

    答:macos原生支持,可以通过脚本卸载再重新挂载为可读写即可,脚本在此

  2. AI学习网址记录

    https://ai.yanxishe.com/ https://ai.yanxishe.com/page/blogDetail/14365 GAN网络 对抗式生成网络-图像超分辨及图像修复 完全可见 ...

  3. Docker Error response from daemon: driver failed programming external connectivity on endpoint webserver

    按照官网的配置, https://docs.docker.com/docker-for-windows/ 当执行到第 5 步时 如果当前你的电脑 80 端口已经被别的程序占用,运行以上命令就会报错,报 ...

  4. Vue非父子组件传值

    <template> <div id="app"> <v-home></v-home> <br> <hr> ...

  5. php报错syntax error, unexpected T_GOTO, expecting T_STRING,报错文件与行数指向以下代码,是什么原因?

    本机php版本是5.3.8,Apache/2.2.21public function goto($url, $msg=NULL) {if ($msg) {$this->jsAlert($msg) ...

  6. SIEBEL BIP报表平台端口被占用

    管理-服务器配置S 找到对应的服务名称: CRMSS2 组件输入:XMLP Report Server 选择[高级]后,查找 Static Port Number 即可修改端口号. 端口号可查询测试环 ...

  7. 使用java 执行ping命令

    借助 Runtime.getRuntime().exec() 可以运行一个windows的exe程序如图,使用java运行 ping 192.168.2.106,返回这样的字符串   1 2 3 4 ...

  8. LeetCode_326. Power of Three

    326. Power of Three Easy Given an integer, write a function to determine if it is a power of three. ...

  9. Delphi XE中使用dbExpress连接MySQL数据库疑难问题解决

    Delphi IDE中包含一个Data Explorer的组件,如下图所示: 该组件基于dbExpress(包含TSQLConnection.TSQLDataSet.TSQLQuery.TSQLSto ...

  10. 【serviceaccount 和 clusterrolebinding】

    kubectl get clusterrolebinding kubectl create clusterrolebinding suosheng-rebinding --clusterrole=cl ...