饱汉模式
1、基础的饱汉模式写法2、Synchronized 关键字解决线程安全问题3、DCL 解决每次都要上锁的问题 1.0 version 饿汉模式
饿汉模式写法
1.1 基础饿汉写法1.2 优化版饿汉写法 Holder 模式1.3 枚举模式写法 总结
饱汉模式饱汉,即已经吃饱了,不着急再吃,饿的时候再吃。
大概意思就是,他不先不初始化单例,等下一次使用的时候再初始化,即 “懒加载”。
饱汉的核心就是懒加载,好处是启动速度快,节省内存资源,到实例被获取时才需要被实例化。
//饱汉 public class FullSingle { private static MySingle mySingle; public static MySingle getMySingle(){ if (null == mySingle){ mySingle = new MySingle(); } return mySingle; } }
基础的写法存在线程安全的问题,大概就是多个并发执行该方法,创建的对象可能都是不同,也就是上面的if条件存在竞争。
2、Synchronized 关键字解决线程安全问题最简单有效的方法使用 synchronized 关键字修饰 getMySingle 方法,这样能达到绝对的线程安全。
//饱汉 public class FullSingle { private static MySingle mySingle; public synchronized static MySingle getMySingle(){ if (null == mySingle){ mySingle = new MySingle(); } return mySingle; } }
这种写法虽然对线程安全有了解决,坏处就是并发的性能极差,可以认为完全退化到了串行。
对象只需创建一次,而创建完之后,后面的每次获取都避免不开 synchronized 锁,从而把这个方法变成串行的 *** 作了。
DCL (double check lock) 双层检查机制
//饱汉 public class FullSingle { private static MySingle mySingle; public static MySingle getMySingle(){ if (null == mySingle){ synchronized (FullSingle.class){ if (null == mySingle){ mySingle = new MySingle(); } } } return mySingle; } }
这种写法的核心是 DCL ,在 synchronized 内层又加了一个 if 判断。就是再创建完单例对象之后,可以避免再次走 synchronized 锁。
该写法存在指令重排的问题,导致对象只初始化了一半。解决方法就是 把 单例对象 修饰为 volatile ,就能禁止指令重排,被称为 DCL v2.0写法
饿汉模式private static volatile MySingle mySingle ;
饿汉很饿,只想着尽早吃到,所以他就在最早的时机,即类加载初始化单例,后面获取的时候直接返回即可。
饿汉模式写法 1.1 基础饿汉写法//饿汉 public class HungrySingle { private static MySingle mySingle = new MySingle(); public static MySingle getMySingle(){ return mySingle; } }
饿汉的缺点就是没有懒加载,会比饱汉式浪费一些资源,启动慢一些,优点就是不会存在线程安全的问题。
1.2 优化版饿汉写法 Holder 模式单线程环境下,俄汉与饱汉在性能上没什么差别;但多线程环境下,由于饱汉需要加锁,饿汉的性能反而更优。
我们即希望饿汉模式中的静态变量的方便和线程的安全,又希望通过懒加载避免浪费资源。Holder 模式满足了这两个优点,核心仍是静态变量,即方便又保证了线程安全,通过静态内部类持有真正的实例,间接的使用了懒加载。
//饿汉 //Holder模式 public class HungrySingle { private static class HungrySingleHolder{ private static final MySingle mySingle = new MySingle(); private HungrySingleHolder(){ } } private HungrySingle() { } public static MySingle getMySingle(){ return HungrySingleHolder.mySingle; } }
相对于基础的饿汉模式写法,Holder 模型增加了静态内部类,性能效果和饱汉的两层校验的写法相当(略优)。
1.3 枚举模式写法用枚举模式实现单例比较方便,但不存在可读性。
基础枚举实现单例
//将枚举的静态变量作为单例的实例 public enum SingleEnum { SINGLE; }
通过反编译打开枚举,就看到了枚举类型的本质,简化如下:
//枚举 public class SingleEnum extends Enum总结{ ... public static final SingleEnum SINGLE = new SingleEnum; ... }
上面考虑的场景,都忽略了反射和序列化的情况。通过反射和序列化能够访问到私有构造器,这样就能破坏单例对象。只有枚举模式能天然防范这一问题。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)