美文网首页
05 - 单例模式

05 - 单例模式

作者: shalk | 来源:发表于2018-06-05 11:45 被阅读0次

    定义: 某一种对象,全局只能创建一个。

    单例模式犹如茴香豆有N中写法。

    1. 构造函数设置成私有
    2. 静态构造方法构造对象。
    public class Singleton {
      private static final Singleton s = new Singleton();
      private Singleton() {
      }
      public static Singleton getInstance() {
            return s;
       }
    }
    

    按照java的语法,都知道静态变量会自动初始化,并且final的变量只会初始化一次并且引用不可改变,这样我们只有一个Singleton对象的引用。

    但是这个Singleton对象什么时候创建出来的呢,什么时候执行的呢?

    反编译一下:

    Classfile /root/java/Singleton.class
      Last modified Jun 4, 2018; size 369 bytes
      MD5 checksum 1820528d8fe309e7c6bff10a17dd9113
      Compiled from "Singleton.java"
    public class Singleton
      minor version: 0
      major version: 52
      flags: ACC_PUBLIC, ACC_SUPER
    Constant pool:
       #1 = Methodref          #5.#17         // java/lang/Object."<init>":()V
       #2 = Fieldref           #3.#18         // Singleton.s:LSingleton;
       #3 = Class              #19            // Singleton
       #4 = Methodref          #3.#17         // Singleton."<init>":()V
       #5 = Class              #20            // java/lang/Object
       #6 = Utf8               s
       #7 = Utf8               LSingleton;
       #8 = Utf8               <init>
       #9 = Utf8               ()V
      #10 = Utf8               Code
      #11 = Utf8               LineNumberTable
      #12 = Utf8               getInstance
      #13 = Utf8               ()LSingleton;
      #14 = Utf8               <clinit>
      #15 = Utf8               SourceFile
      #16 = Utf8               Singleton.java
      #17 = NameAndType        #8:#9          // "<init>":()V
      #18 = NameAndType        #6:#7          // s:LSingleton;
      #19 = Utf8               Singleton
      #20 = Utf8               java/lang/Object
    {
      public static Singleton getInstance();
        descriptor: ()LSingleton;
        flags: ACC_PUBLIC, ACC_STATIC
        Code:
          stack=1, locals=0, args_size=0
             0: getstatic     #2                  // Field s:LSingleton;
             3: areturn
          LineNumberTable:
            line 6: 0
    
      static {};
        descriptor: ()V
        flags: ACC_STATIC
        Code:
          stack=2, locals=0, args_size=0
             0: new           #3                  // class Singleton
             3: dup
             4: invokespecial #4                  // Method "<init>":()V
             7: putstatic     #2                  // Field s:LSingleton;
            10: return
          LineNumberTable:
            line 2: 0
    }
    SourceFile: "Singleton.java"
    

    可以看到,s的初始化放到了static()方法里,也就是类构造器(cinit);

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

    这个方法什么时候执行呢?
    类初始化的时候

    类的生命周期是:
    加载,验证,准备,解析,初始化,使用,卸载。

    当使用的时候,一般都是 Singleton a = Singleton.getInstance(); 此时遇到了指令invokestatic ,类必须要初始化,Singleton类 进行加载,验证,准备(清零),解析(接口,字段解析),初始化。

    jvm在执行cinit方法时,会采取加锁和同步的方式。避免其他线程也执行,并且在本线程执行完毕后,其他线程也无法执行。

    因此该对象的创建是线程安全的。

    大多数情况下,这样设计就可以的。


    进一步要考虑的是懒加载(调用的时候再创建,不要再加载的时候创建),线程安全,以及序列化安全。

    如果创建该对象的时间很长,可以采用一些办法,在使用的时候再创建。
    内部类懒加载

    public class Single {
      private Single() {
     }
      private static class Holder {
          private final static Single s = new Single();
      }
      public static Single getInstance() {
         return Holder.s;
      }
    }
    

    这样Single类如果因为其他情况,发生了加载,也不会初始化s。
    只有调用getInstance() 时,加载Single类,执行Holder.s ,对应指令是invokestatic,加载Holder类,初始化s,并返回。这个过程是jvm控制加锁,线程安全。


    序列化安全使用枚举即可,避免反射和序列化的攻击。
    不过jdk打算把序列化从jdk中移除出去,JDK认为有至少三分之一的安全问题都是Serializable引起的。

    END

    相关文章

      网友评论

          本文标题:05 - 单例模式

          本文链接:https://www.haomeiwen.com/subject/pxdpsftx.html