并发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(); } }
安全、性能好、逼格高、懒汉。
扩展:内部类特性,懒加载。