单例

作者: 刘尔泽 | 来源:发表于2017-10-14 16:36 被阅读5次

单例介绍

单例 模式 是一种对象的创建模式,它用于生产一个对象的具体实例,它看可以确保系统中一个类只才产生一个实例。。 实现的单例是在虚拟机的范围内,因为装在类ClassLoader 他是虚拟机的,一个虚拟机在通过自己的 classload 装载实现单例类的时候,就会创建一个类的实例。 在java 中:
1 对于频繁使用的对象,可以省略创建对象所花费的时间。
2 减少 new -----》减少 gc ,

单例的六种

  • 饿汉模式
    有一个private 的构造函数,mInstance 是 static 的,同事他的getMInstance 也是 static的。
    不能 对 mInstance 做延迟加载,比如单例的创建过程有时候会很慢,变量初始化内容特别多时候,所以在加载这个类的时候,就会被执行,任何用得到类的地方就会初始化这个变量。
public class HungurySingleton {
    private static final HungurySingleton mHungurySingleton = new HungurySingleton();
    private HungurySingleton(){
        System.out.println("Singleton is create");
        
    }
    public static HungurySingleton getHungurySingleton() {
        return mHungurySingleton;
    }
    
    public static void createString(){
         System.out.println("createString in Singleton");
    }
          
    public static void main(String[] args){
        HungurySingleton.createString();
    }
}
  • 懒汉模式
public class LazySingleton {
    private static LazySingleton instance;
    private LazySingleton() {
    }
    public static LazySingleton getInstance() {
        // 第一次调用的时候会被初始化
        if (instance == null) {
            instance = new LazySingleton();
        }
        return instance;
    }
    
    public static void createString(){
         System.out.println("create String");
    }
          
    public static void main(String[] args){
         LazySingleton.createString();
    }
}

多线程并发下,无法保持 实例是唯一的。
因为getInstance 不是同步的

所以做 懒汉线程安全问题的 单例

   //方法中声明synchronized关键字
   public static synchronized LazySafetySingleton getInstance() {
     if (instance == null) {  
         instance = new LazySafetySingleton();  
     }  
     return instance;  
   }
   
   //同步代码块实现
   public static LazySafetySingleton getInstance1() {  
           synchronized (LazySafetySingleton.class) {  
               if(instance == null){//懒汉式   
                   instance = new LazySafetySingleton();  
               }  
           }  
       return instance;  
   }

以上 性能有问题。
优化:DCL
双检查锁机制。。

public class DclSingleton {
    private static volatile DclSingleton mInstance = null;
//  private static DclSingleton mInstance = null;
    private DclSingleton() {
    }
    public void doSomething() {
        System.out.println("do sth.");
    }
    public static DclSingleton getInstance() {
        // 避免不必要的同步
        if (mInstance == null) {
            // 同步
            synchronized (DclSingleton.class) {
                // 在第一次调用时初始化
                if (mInstance == null) {
                    mInstance = new DclSingleton();
                }
            }
        }
        return mInstance;
    }
}

mInstance = new DclSingleton(); 这不是一个原子操作

原子操作(atomic operation)是不需要synchronized",这是Java多线程编程的老生常谈了。所谓原子操作是指不会被[线程调度]机制打断的操作;这种操作一旦开始,就一直运行到结束,中间不会有任何 context switch 切 换到另一个线程
new 过程 是 先创建内存,然后执行 构造方法,然后指向 那块内存。
但是JVM在即时编译器中,会存在一个指令重排的优化,也就是以上说的三步,他不会按照我们想要的顺序执行,有可能第一步在 第二步之前。
所以把, mINstance 使用 volatile 关键字就好了。
因为它 的可见性,也就说他能保证线程在本地不会存在 instance 的副本,而每次都会去主内存中读取,防止指令重排。

JVM 的即时编译器中存在指令重排的优化

静态内部类

JVM 提供给我们的同步控制
static final

public class StaticInnerSingleton {
    private StaticInnerSingleton() {
    }
    public static StaticInnerSingleton getInstance() {
        return SingletonHolder.sInstance;
    }
    // 静态内部类
    private static class SingletonHolder {
        private static final StaticInnerSingleton sInstance = new StaticInnerSingleton();
    }
}

调用构造方法时,外部类Outer被加载,但这时其静态内部类StaticInner却未被加载。直到调用该内部类的静态方法(在分割线以下),StaticInner才被加载。可以做类似的实验验证非静态内部类的情况。
结论:加载一个类时,其内部类不会同时被加载。一个类被加载,当且仅当其某个静态成员(静态域、构造器、静态方法等)被调用时发生

内部类(不论是静态内部类还是非静态内部类)都是在第一次使用时才会被加载。 对于非静态内部类是不能出现静态模块(包含静态块,静态属性,静态方法等) 非静态类的使用需要依赖于外部类的对象

保证了线程安全,保证了性能。
但真正保证线程安全的原因是,虚拟机加载内部类的时候 classloader 就已经给加锁了

枚举

public enum EnumSingleton {
     //定义一个枚举的元素,它就是 Singleton 的一个实例
    INSTANCE;  
    
    public void doSomeThing() {  
         // do something...
    }  
}


枚举

 enum Type{
    A,B,C,D;

    static int value;
    public static int getValue() {
        return value;
    }

    String type;
    public String getType() {
        return type;
    }
}

在原有的基础上,添加了类方法和实例方法。我们把Type看做一个类,
那么enum中静态的域和方法,都可以视作类方法。和我们调用普通的静态方法一样,
这里调用类方法也是通过  Type.getValue()即可调用,访问类属性也是通过Type.value即可访问。

下面的是实例方法,也就是每个实例才能调用的方法。那么实例是什么呢?
没错,就是A,B,C,D。所以我们调用实例方法,也就通过 Type.A.getType()来调用就可以了。
最后,对于某个实例而言,还可以实现自己的实例方法。再看下下面的代码:

enum Type{
A{
    public String getType() {
        return "I will not tell you";
    }
},B,C,D;
static int value;

public static int getValue() {
    return value;
}

String type;
public String getType() {
    return type;
 }
}

枚举实现单例

class Resource{
}

public enum SomeThing {
    INSTANCE;
    private Resource instance;
    SomeThing() {
        instance = new Resource();
    }
    public Resource getInstance() {
        return instance;
    }
}

上面的类Resource是我们要应用单例模式的资源,具体可以表现为网络连接,数据库连接,线程池等等。
获取资源的方式很简单,只要 SomeThing.INSTANCE.getInstance() 即可获得所要实例。下面我们来看看单例是如何被保证的:
首先,在枚举中我们明确了构造方法限制为私有,在我们访问枚举实例时会执行构造方法,同时每个枚举实例都是static final类型的,也就表明只能被实例化一次。在调用构造方法时,我们的单例被实例化。
也就是说,因为enum中的实例被保证只会被实例化一次,所以我们的INSTANCE也被保证实例化一次。

Android 中的 单例

  • application

相关文章

  • Android设计模式总结

    单例模式:饿汉单例模式://饿汉单例模式 懒汉单例模式: Double CheckLock(DCL)实现单例 Bu...

  • IOS单例模式的底层原理

    单例介绍 本文源码下载地址 1.什么是单例 说到单例首先要提到单例模式,因为单例模式是单例存在的目的 单例模式是一...

  • 【设计模式】单例模式

    单例模式 常用单例模式: 懒汉单例模式: 静态内部类单例模式: Android Application 中使用单例模式:

  • 2020-11-02-Spring单例 vs. 单例模式

    Spring 单例不是 Java 单例。本文讨论 Spring 的单例与单例模式的区别。 前言 单例是 Sprin...

  • IOS学习笔记之单例

    单例介绍 1.什么是单例 说到单例首先要提到单例模式,因为单例模式是单例存在的目的 单例模式是一种常用的软件设计模...

  • OC - 单例模式

    导读: 一、什么是单例模式 二、单例的作用 三、常见的单例类 四、自定义单例类的方法 一、什么是单例模式 单例模式...

  • 单例

    单例 单例宏

  • 单例模式

    特点 单例类只有1个实例对象 该单例对象必须由单例类自行创建 单例类对外提供一个访问该单例的全局访问点 结构 单例...

  • 关于java单例模式,这篇已经讲得很清楚了,建议收藏!

    概念 java中单例模式是一种常见的设计模式,单例模式分三种:懒汉式单例、饿汉式单例、登记式单例三种。 特点 单例...

  • 单例

    iOS单例模式iOS之单例模式初探iOS单例详解

网友评论

      本文标题:单例

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