精华内容
下载资源
问答
  • 深入理解单例模式静态内部类单例原理

    万次阅读 多人点赞 2018-05-26 02:00:54
    本文主要介绍java的单例模式,以及详细剖析静态内部类之所以能够实现单例的原理。OK,废话不多说,进入正文。 首先我们要先了解下单例的四大原则: 1.构造私有。 2.以静态方法或者枚举返回实例。 3.确保实例只有...

    本文主要介绍java的单例模式,以及详细剖析静态内部类之所以能够实现单例的原理。OK,废话不多说,进入正文。

    首先我们要先了解下单例的四大原则:

    1.构造私有。
    2.以静态方法或者枚举返回实例。

    3.确保实例只有一个,尤其是多线程环境。

    4.确保反序列换时不会重新构建对象。

    我们常用的单例模式有:

    饿汉模式、懒汉模式、双重锁懒汉模式、静态内部类模式、枚举模式,我们来逐一分析下这些模式的区别。

    1.饿汉模式:

    public class SingleTon{
       private static SingleTon INSTANCE = new SingleTon();
     private SingleTon(){}
    public static SingleTon getInstance(){ return INSTANCE; }}

    饿汉模式在类被初始化时就已经在内存中创建了对象,以空间换时间,故不存在线程安全问题。

    2.懒汉模式:

    public class SingleTon{
       private static SingleTon  INSTANCE = null;
       private SingleTon(){}
       public static SingleTon getInstance() {  
       if(INSTANCE == null){
          INSTANCE = new SingleTon(); 
        } 
        return INSTANCE;
      }
    }

    懒汉模式在方法被调用后才创建对象,以时间换空间,在多线程环境下存在风险

    3.双重锁懒汉模式(Double Check Lock)

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

     

    DCL模式的优点就是,只有在对象需要被使用时才创建,第一次判断 INSTANCE == null为了避免非必要加锁,当第一次加载时才对实例进行加锁再实例化。这样既可以节约内存空间,又可以保证线程安全。但是,由于jvm存在乱序执行功能,DCL也会出现线程不安全的情况。具体分析如下:

     

     

    INSTANCE  = new SingleTon(); 

    这个步骤,其实在jvm里面的执行分为三步:

              1.在堆内存开辟内存空间。
      2.在堆内存中实例化SingleTon里面的各个参数。
      3.把对象指向堆内存空间。

    由于jvm存在乱序执行功能,所以可能在2还没执行时就先执行了3,如果此时再被切换到线程B上,由于执行了3,INSTANCE 已经非空了,会被直接拿出来用,这样的话,就会出现异常。这个就是著名的DCL失效问题

    不过在JDK1.5之后,官方也发现了这个问题,故而具体化了volatile,即在JDK1.6及以后,只要定义为private volatile static SingleTon  INSTANCE = null;就可解决DCL失效问题。volatile确保INSTANCE每次均在主内存中读取,这样虽然会牺牲一点效率,但也无伤大雅。

    3.静态内部类模式:

    public class SingleTon{
      private SingleTon(){}
    
      private static class SingleTonHoler{
         private static SingleTon INSTANCE = new SingleTon();
     }
    
      public static SingleTon getInstance(){
        return SingleTonHoler.INSTANCE;
      }
    }

    静态内部类的优点是:外部类加载时并不需要立即加载内部类,内部类不被加载则不去初始化INSTANCE,故而不占内存。即当SingleTon第一次被加载时,并不需要去加载SingleTonHoler,只有当getInstance()方法第一次被调用时,才会去初始化INSTANCE,第一次调用getInstance()方法会导致虚拟机加载SingleTonHoler类,这种方法不仅能确保线程安全,也能保证单例的唯一性,同时也延迟了单例的实例化。

    那么,静态内部类又是如何实现线程安全的呢?首先,我们先了解下类的加载时机。


    类加载时机:JAVA虚拟机在有且仅有的5种场景下会对类进行初始化。
    1.遇到new、getstatic、setstatic或者invokestatic这4个字节码指令时,对应的java代码场景为:new一个关键字或者一个实例化对象时、读取或设置一个静态字段时(final修饰、已在编译期把结果放入常量池的除外)、调用一个类的静态方法时。
    2.使用java.lang.reflect包的方法对类进行反射调用的时候,如果类没进行初始化,需要先调用其初始化方法进行初始化。
    3.当初始化一个类时,如果其父类还未进行初始化,会先触发其父类的初始化。
    4.当虚拟机启动时,用户需要指定一个要执行的主类(包含main()方法的类),虚拟机会先初始化这个类。
    5.当使用JDK 1.7等动态语言支持时,如果一个java.lang.invoke.MethodHandle实例最后的解析结果REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,并且这个方法句柄所对应的类没有进行过初始化,则需要先触发其初始化。
    这5种情况被称为是类的主动引用,注意,这里《虚拟机规范》中使用的限定词是"有且仅有",那么,除此之外的所有引用类都不会对类进行初始化,称为被动引用。静态内部类就属于被动引用的行列。

     

    我们再回头看下getInstance()方法,调用的是SingleTonHoler.INSTANCE,取的是SingleTonHoler里的INSTANCE对象,跟上面那个DCL方法不同的是,getInstance()方法并没有多次去new对象,故不管多少个线程去调用getInstance()方法,取的都是同一个INSTANCE对象,而不用去重新创建。当getInstance()方法被调用时,SingleTonHoler才在SingleTon的运行时常量池里,把符号引用替换为直接引用,这时静态对象INSTANCE也真正被创建,然后再被getInstance()方法返回出去,这点同饿汉模式。那么INSTANCE在创建过程中又是如何保证线程安全的呢?在《深入理解JAVA虚拟机》中,有这么一句话:

     虚拟机会保证一个类的<clinit>()方法在多线程环境中被正确地加锁、同步,如果多个线程同时去初始化一个类,那么只会有一个线程去执行这个类的<clinit>()方法,其他线程都需要阻塞等待,直到活动线程执行<clinit>()方法完毕。如果在一个类的<clinit>()方法中有耗时很长的操作,就可能造成多个进程阻塞(需要注意的是,其他线程虽然会被阻塞,但如果执行<clinit>()方法后,其他线程唤醒之后不会再次进入<clinit>()方法。同一个加载器下,一个类型只会初始化一次。),在实际应用中,这种阻塞往往是很隐蔽的。

    故而,可以看出INSTANCE在创建过程中是线程安全的,所以说静态内部类形式的单例可保证线程安全,也能保证单例的唯一性,同时也延迟了单例的实例化。

    那么,是不是可以说静态内部类单例就是最完美的单例模式了呢?其实不然,静态内部类也有着一个致命的缺点,就是传参的问题,由于是静态内部类的形式去创建单例的,故外部无法传递参数进去,例如Context这种参数,所以,我们创建单例时,可以在静态内部类与DCL模式里自己斟酌。

    最后粗略的介绍下枚举类型的单例吧。

    枚举单例:

    public enum SingleTon{
      INSTANCE;
            public void method(){
            //TODO
         }
    }

    枚举在java中与普通类一样,都能拥有字段与方法,而且枚举实例创建是线程安全的,在任何情况下,它都是一个单例。我们可直接以

    SingleTon.INSTANCE

    的方式调用。

    参考资料:
    《深入理解JAVA虚拟机》
    《Android源码设计模式解析与实战》
    《java虚拟机规范》

     

     

    展开全文
  • 而饿汉模式不能延迟实例化对象,下面是一个静态内部类单例模式的实现,既保证了线程的安全,有能够延迟加载,也就是在第一次使用的时候加载。实现package com.helper.androidcommon; /** * Created by Shinlone on

    简介

    单例模式是一种常见的模式,懒汉模式考虑线程安全需要在获取单例的方法添加synchronized关键字实现同步代码块,这样造成了性能损耗;而饿汉模式不能延迟实例化对象,下面是一个静态内部类单例模式的实现,既保证了线程的安全,有能够延迟加载,也就是在第一次使用的时候加载。

    实现

    package com.helper.androidcommon;
    /**
     * Created by Shinlone on 2016/8/16.
     */
    public class Singleton {
    
        private Singleton() {}
    
        /**
         * 获取实例
         *
         * @return
         */
        public static Singleton getInstance() {
            return SingletonHolder.sIntstance;
        }
    
        /**
         * 动作A
         */
        public void actionA() {
    
        }
    
        /**
         * 动作B
         */
        public void actionB() {
    
        }
    
        /**
         * 一个私有的静态内部类,用于初始化一个静态final实例
         */
        private static class SingletonHolder {
            private static final Singleton sIntstance = new Singleton();
        }
    }
    

    使用

    Singleton.getInstance().actionA();//获取实例使用方法A
    Singleton.getInstance().actionB();//获取实例使用方法B
    
    展开全文
  • 静态内部类实现单例模式1、中代码块和静态代码块的执行 测试的内部类和外部 public class TestNestedClass { static String name = "name"; { System.out.println("外部静态代码块执行"); } static{ ...

    静态内部类实现单例模式

    1、类中代码块和静态代码块的执行

    测试的内部类和外部类
    public class TestNestedClass {
    
        static String name = "name";
    
        {
            System.out.println("外部类非静态代码块执行");
        }
    
        static{
            System.out.println("外部类静态代码块执行");
        }
    
        public static class Nested{
    
            static String nesetdName = "NestedName";
    
            {
                System.out.println("内部类非静态代码块执行");
            }
    
            static{
                System.out.println("内部类静态代码块执行");
            }
        }
    }
    测试类 
    public class TestDemo {
    
        public static void main(String[] args) throws SQLException {
    
            TestNestedClass test = new TestNestedClass();
            TestNestedClass tes1t = new TestNestedClass();
    
            //System.out.println(TestNestedClass.Nested.nesetdName);
        }
    }
    输出结果:
    外部类静态代码块执行
    外部类非静态代码块执行
    外部类非静态代码块执行
    将注释的那句话取消注释输出结果为:
    外部类静态代码块执行
    外部类非静态代码块执行
    外部类非静态代码块执行
    内部类静态代码块执行
    NestedName

    结论:new对象时静态代码块和代码块都会执行,但是静态代码块只会执行一次,代码块次数为创建对象的次数;调用类中的静态成员时,代码块不执行,静态代码块执行。

    2、通过上面结论使用静态内部类实现单例模式

    public class SingleInstanceTest {
    
        private SingleInstanceTest(){}
    
        static class SingleInstance{
            private static SingleInstanceTest instance 
                = new SingleInstanceTest();
        }
    
        public static SingleInstanceTest getInstance(){
            return SingleInstance.instance;
        }
    
    }

    在使用静态内部类中的静态成员时执行一次,此时new创建一个对象,之后在调用不会多次创建,所以只能创建 一个对象,实现单例模式要求。

    展开全文
  • JAVA设计模式之单例模式内部静态类 经典示例 三个要点JAVA设计模式之单例模式内部静态类 单例模式最初的定义出现于《设计模式》(艾迪生维斯理, 1994):“保证一个类仅有一个实例,并提供一个访问它的全局访问点...

    JAVA设计模式之单例模式:内部静态类


    • 单例模式最初的定义出现于《设计模式》(艾迪生维斯理, 1994):“保证一个类仅有一个实例,并提供一个访问它的全局访问点。”
    • Java中单例模式定义:“一个类有且仅有一个实例,并且自行实例化向整个系统提供。”1

    经典示例

    package com.djq.singleton;
    
    public class Singleton {
    
        private Singleton(){}
    
        private static class LazySingleton{
            private static final Singleton singleton = new Singleton();
        }
    
        public static final Singleton getInstance(){
            return LazySingleton.singleton;
        }
    }
    

    三个要点

    1. 是某个类只能有一个实例;
    2. 是它必须自行创建这个实例;
    3. 是它必须自行向整个系统提供这个实例。

    从具体实现角度来说,就是以下三点:一是单例模式的类只提供私有的构造函数,二是类定义中含有一个该类的静态私有对象,三是该类提供了一个静态的公有的函数用于创建或获取它本身的静态私有对象。— [百度百科]


    原文作者:Dolphix.J Qing
    原文地址http://blog.csdn.net/dolphixj

    展开全文
  • 静态内部类实现单例模式 public class Singleton { private Singleton() { } public static Singleton getInstance() { return SingletonHolder.instance; } private static class SingletonHolder { ...
  • 使用静态内部类实现单例模式

    千次阅读 2017-05-05 10:20:06
    代码如下: /** * Created on 2017/5/5. ... * Description:使用静态内部类的方式实现单例模式 */ public class Singleton { private Singleton() { } public static final Singleton getInstance() {
  • 单例模式——静态内部类单例原理 了解下单例的四大原则: 1.构造私有。 2.以静态方法或者枚举返回实例。 3.确保实例只有一个,尤其是多线程环境。 4.确保反序列化时不会重新构建对象。 我们常用的单例模式有:...
  • 基础知识: Java程序的初始化顺序依次是:父类静态变量-->父类静态代码块-->...用静态内部类实现单例模式的原理:静态内部类可以不依赖外部的实例而被实例化。(更多关于静态内部类的内容,...
  • 静态内部类和非静态内部类与外部...静态内部类的初始化不依赖于外部,又因为的初始化过程中是线程安全的,所以可以实现单例模式。 而对于非静态内部类而言,由于需要依赖外部的初始化,所以实现不了单例模式 ...
  • 剑指Offer Java实现 - 单例模式 饿汉式 懒汉式 双重校验 内部静态类 单例模式 单例模式可以说是Java设计模式中最简单的一个了,它属于创建型模式。单例模式只涉及到一个单一的类,其中心思想就是由该类负责创建...
  • 一、什么是嵌套 嵌套是指被定义在另一个内部。...二、什么是静态内部类 静态成员是最简单的一种嵌套。最好把他看做是普通,只是碰巧被声明在另一个内部而已,它可以访问外围的所有成...
  • DCL虽然在一定程度上解决了资源消耗、多余的同步、现成安全问题,但是还是在某些情况下会...这种方法不仅能保证现成安全,也能够保证对象的唯一性,同事也延迟了单例的实例化,所以这是推荐使用的单例模式实现方式。
  • 文章目录一、用途二、单例四大原则三、几种实现方式1、饿汉式2、DCL单例3、静态内部类单例4、枚举单例1) effective java 书中推荐使用的单例模式;2) 不会受到反序列化、反射的破坏;3) 枚举编译后是一个继承自java....
  • 加载一个时,其内部类是否同时被加载?下面我们做一个实验来看一下。  Java代码  public class Outer {   static {   System.out.println("load ... //静态内部类   static class
  • * 级的内部类,也就是静态的成员式内部类,该内部类的实例与外部的实例 * 没有绑定关系,而且只有被调用到才会装载,从而实现了延迟加载 */ private static class SingletonHolder { /** * 静态初始化器,...
  • 单例模式饿汉式与懒汉式,内部类实现单例模式

    万次阅读 多人点赞 2020-11-08 19:17:07
    单例模式(Singleton Pattern)是指确保一个在任何情况下都绝对持有一个实例,并提供一个全局访问点。 饿汉式单例模式 就是在加载的时候就立即初始化,并且创建单例对象。绝对的线程安全 public class ...
  • 加载一个时,其内部类是否同时被加载?下面我们做一个实验来看一下。 Java代码 public class Outer { static { System.out.println("load outer class..... } //静态内部类 static class StaticInner { stat

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 82,159
精华内容 32,863
关键字:

内部静态类单例模式