并发4-单例模式

模式并不是只有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);
    }

}  

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

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

 

 

  懒汉式:延时加载

  线程安全性:

  性能:对于内存来说比较好,因为用到才创建,但是由于使用了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方法分析

HoonSingleton的getInstance1方法的分析

 

  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规则带来的困扰

 

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

}

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

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

 

posted @ 2019-03-20 20:17  为了WZJ  阅读(170)  评论(0编辑  收藏  举报