单例模式中的饿汉模式和懒汉模式【一看就懂】
以Java为例:
饿汉:
public final class VirtualCore { private static VirtualCore gCore = new VirtualCore(); private VirtualCore() { } public static VirtualCore get() { return gCore; } }
懒汉:
public final class VirtualCore { private static VirtualCore gCore = null; private VirtualCore() { } public static synchronized VirtualCore get() { if(gCore == null){ gCore = new VirtualCore(); } return gCore; } }
先来看单例模式原理及要求,保证这个类在内存中只有一个对象,那么就不能随便给别人new,所以必须把构造函数改为private,然后整一个公共静态方法供外部统一获取实例。
再来看饿汉以及懒汉定义(原理)以及区别:
饿汉:一开始就吧吃的找好(对象new出来),随时可以吃
懒汉:懒得动,饿了(有需要)再去找吃的(new 对象)
两者区别在于,饿汉模式拿空间换时间,一开始就把对象生成,在内存中占着,懒汉则是按需生成。注意的是,多线程访问的时候,
懒汉可能会因为不同步创建多个对象,所以聪明的人们在获取单例的方法加上 synchronized字段~
public final class VirtualCore { private static VirtualCore gCore = null; private VirtualCore() { } public static synchronized VirtualCore get() { if(gCore == null){ synchronized(VirtualCore.class){ if(gCore == null){ gCore = new VirtualCore(); } } } return gCore; } }
这就是 DoubleCheckLock(DCL)双检锁,DCL虽然在一定程度上解决了资源消耗、多余的同步、线程安全等问题,但是,
它还是在某些情况下会出现失效的问题。这个问题被称为双重检查锁定(DCL)失效,在《Java并发编程实践》一书的最后
谈到了这个问题,并指出这种“优化”是丑陋的,不赞成使用。而建议使用如下的代码替代:
1 public class VirtualCore { 2 private VirtualCore(){} 3 public static VirtualCore getInstance(){ 4 return VirtualCoreHolder.gCore; 5 } 6 private static class VirtualCoreHolder{ 7 private static final VirtualCore gCore = new VirtualCore(); 8 } 9 }
当第一次加载VirtualCore类时并不会初始化gCore,只有在第一次调用VirtualCore的getInstance方法时才会导致gCore被初始化。因此第一次调用getInstance方法会导致
虚拟机加载VirtualCoreHolder类,这种方式不仅能够确保线程安全,也能够保证单例的实例化,所以这是推荐使用的单例模式实现方式!