设计模式之单例模式
单例模式,是设计模式中最简单的一种。通过单例模式可以保证系统中的某个类只有一个实例而且该实例易于外界访问,从而方便对实例个数的控制并节约系统资源。如果希望让系统中某个类的对象只能存在一个,单例模式就是最好的解决方案。但是,单例模式真的能够实现实例的唯一性吗?
答案是否定的,很多人都知道使用反射可以破坏单例模式,除了反射以外,使用序列化与反序列化也同样会破坏单例。
比如创建一个User 单例:
这种创建方式就是常说的饿汉模式:在User类被JVM加载的时候就创建好一个唯一的实例;以后都是用这个实例;同时,由于该实例在类被加载的时候就创建出来了,所以也避免了线程安全问题。
还有一种饿汉模式的变种:
这两种方法都是在jvm加载类的时候就是实例对象;本质是一样的;
由于饿汉模式是在类被加载的时候对象就会实例化。这也许会造成不必要的消耗,因为有可能这个实例根本就不会被用到。而且,如果这个类被多次加载的话也会造成多次实例化。其实解决这个问题的方式有很多,下面提供两种解决方式,第一种是使用静态内部类的形式。第二种是使用懒汉式。
这种方式同样利用了classloder的机制来保证初始化User时只有一个线程,它跟饿汉式不同的是(很细微的差别):饿汉式是只要User类被装载了,那么user实例就会被实例化(没有达到lazy loading效果),而这种方式是User类被装载了,instance不一定被初始化。因为UserSingleton类没有被主动使用,只有显示通过调用Instance方法时,才会显示装载UserSingleton类,从而实例化user。想象一下,如果实例化user很消耗资源,我想让他延迟加载,另外一方面,我不希望在Singleton类加载时就实例化,因为我不能确保User类还可能在其他的地方被主动使用从而被加载,那么这个时候实例化user显然是不合适的。这个时候,这种方式相比饿汉式更加合理。
懒汉模式:
上面这种单例叫做懒汉式单例。懒汉,就是不会提前把实例创建出来,将类对自己的实例化延迟到第一次被引用的时候。Instance方法的作用是希望该对象在第一次被使用的时候被new出来。
这种写法能够在多线程中很好的工作,而且看起来它也具备很好的延迟加载,但是,遗憾的是,他效率很低,因为99%情况下不需要同步。(因为上面的synchronized的加锁范围是整个方法,该方法的所有操作都是同步进行的,但是对于非第一次创建对象的情况,也就是没有进入if语句中的情况,根本不需要同步操作,可以直接返回user。)
通过使用同步代码块的方式减小了锁的范围。这样可以大大提高效率。(对于已经存在user的情况,无须同步,直接return)。