模式并不是只有java才有,它是一种思路。

 

为什么要用单例?

多个线程操作同一个对象就要用到单例。保证对象的唯一性

 

如何解决这个问题?

实例化的过程只实例化一次。多个线程开始到销毁到结束都用到同一个实例对象,提供返回实例对象的方法。

 

单例模式需要考虑的事项:线程的安全性、性能、懒加载(lazy:延迟加载)

单例的分类:

  饿汉式:没有延时加载

  线程安全性:在加载的时候已经被实例化(值类型直接放到工作空间,引用类型是将地址放到工作空间,引用类型的实例是放到主内存中),所以只有这一次,线程是安全的

  性能:因为是静态的属性,虚拟机启动的时候就会实例化,所以用成员变量比较少的时候可以使用饿汉式单例,否则成员变量比较多会占用大量内存空间

public class HungerSingeton {
    //加载的时候产生的实力对象
    private static HungerSingeton Instance = new HungerSingeton();

    private HungerSingeton(){

    }
    //返回的实例对象
    public static HungerSingeton getInstance(){
        return Instance;
    }
    //测试
    public static void main(String args[]){
        HungerSingeton hungerSingeton = getInstance();
        System.out.println(hungerSingeton);
        HungerSingeton hungerSingeton1 = getInstance();
        System.out.println(hungerSingeton1);
    }

}

测试结果:得到是相同的对象

android单例模式同步锁的意义 单例模式 并发_实例化

  多线程测试结果:结果也是一样的

android单例模式同步锁的意义 单例模式 并发_android单例模式同步锁的意义_02

 

 

  懒汉式:延时加载

  线程安全性:

  性能:对于内存来说比较好,因为用到才创建,但是由于使用了Synchronized它退化到串行执行

 

public class HoonSingleton {
    //私有静态
    private static HoonSingleton instance = null;
    //私有构造
    private HoonSingleton(){

    }
    //返回的实例对象,什么使用
    public synchronized static HoonSingleton getInstance(){
        if(null == instance){
            instance = new HoonSingleton();
        }
        return instance;
    }
  
   //改造后让锁的范围更小,只锁对象,而不锁方法,这样多线程访问这个方法的时候会都用有此方法的访问权,但是获取instance的时候会进行排队使用,但是此时还是不保证原子性
public  static HoonSingleton getInstance1(){
      if(null == instance){
          synchronized(HoonSingleton.class){
              instance = new HoonSingleton();
          }
      }
      return instance;
  }
//测试
    public static void main(String args[]){
        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                HoonSingleton hoonSingleton = getInstance();
                System.out.println(hoonSingleton);
            },"getInstance").start();
        }
    }
}

  

下图出现的这种情况将不保证原子性,有可能创建两个实例。

HoonSingleton的getInstance方法分析

android单例模式同步锁的意义 单例模式 并发_实例化_03

HoonSingleton的getInstance1方法的分析

android单例模式同步锁的意义 单例模式 并发_android单例模式同步锁的意义_04

 

  DCLSingleton(Double-Check-Locking)式

  安全性:保证了原子性

  性能:与懒汉是一致,但是DCL模式会引起指令重排(happens-befor),空指针异常。

public class DCL {
    //私有静态
    private volatile static DCL instance = null;
    //私有构造
    private DCL(){

    }
    //返回的实例对象,什么使用
    public  static DCL getInstance(){
        if(null == instance){
            synchronized(DCL.class){
                if(null == instance)
                    instance = new DCL();
            }
        }
        return instance;
    }
    //测试
    public static void main(String args[]){
        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                DCL dcl = getInstance();
                System.out.println(dcl);
            },"getInstance").start();
        }
    }
}

  

 

Volatile+DCL完美解决happens-befor规则带来的困扰

android单例模式同步锁的意义 单例模式 并发_System_05

 

  Holder模式

  声明类的时候,成员变量中不声明实例变量,而放到内部静态类中

  

public class HolderDome {
  
  private HolderDome(){
  }

    private static class Hoder{
        private static HolderDome instance = new HolderDome();
    }

    //保证了原子性,懒加载,性能好,懒加载,内部类只有调用的时候才会创建
    public static HolderDome getInstance(){
        return Hoder.instance;
    }
}

  目前应用比较广泛的一种单例模式

  

  枚举模式

  

public class EnumDomeSingleton {
    //私有构造
    private EnumDomeSingleton(){

    }
    //建立单例返回值
    public static EnumDomeSingleton getInstance(){
        return EnumHolder.INSTANCE.instance;
    }

    //内部枚举
    private enum EnumHolder{
        //定义EnumHolder枚举类型的常量
        INSTANCE;

        //定义需要单例的对象
        private EnumDomeSingleton instance;

        //定义枚举构造,在使用的时候才会创建实例
        private EnumHolder(){
            instance = new EnumDomeSingleton();
        }

        //创建返回的单例实例化
        private  EnumDomeSingleton getInstance(){
            return instance;
        }

    }

    //测试
    public static void main(String args[]){
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                EnumDomeSingleton enumDomeSingleton = EnumDomeSingleton.getInstance();
                System.out.println(enumDomeSingleton);
            }
        }).start();
    }

}

 安全、性能好、逼格高、懒汉。

    扩展:内部类特性,懒加载。