单例模式(Singleton Pattern)
以下内容大部分出自《设计模式》这本书
单例模式可以说是所有设计模式中最简单的模式,只需要一个类就可以完成。
单例模式定义:确保一个类只有一个实例,并提供一个全局访问点。
单例模式有什么用?
有一些对象我们只需要一个就够了,比如:线程池、缓存、对话框、用户信息设置等,这些对象只能有一个实例,如果出现多个实例可能到导致许多问题产生,例如:程序行为异常、资源使用过量、或者是不一致的结果。
单例模式是确保只有一个实例会被创建。单例模式以给了我们一个全局的访问点,和全局变量一样方便,又没有全局变量的缺点。全局变量的缺点即程序一开始就要创建好对象,如果这个对象非常消耗资源,而我们又不经常用到它,这就形成了浪费资源。
下面首先来写一种简单的单例模式
public class MyClass { /** * 利用静态变量来记录MyClass类的唯一实例 */ private static MyClass myClass; /** * 这里的构造函数私有化,防止别的类调用,只有内部才能调用来实例化 */ private MyClass(){} /** * 用getInstance()方法来实例化对象,并返回一个实例 * @return */ public static MyClass getInstance(){ /** * 由于myClass是个静态变量,如果它为空表示该实例还没有被创建,我们就需要创建这个实例 * 如果myClass不为空,就表示该实例已经存在,直接return myClass; */ if(null==myClass){ myClass=new MyClass(); } return myClass; } }在使用的时候只需要调用 MyClass.getInstance()就可以获得到MyClass实例,也可以一定程度上防止MyClass多次被创建。但它是有缺陷的。
在多线程操作的时候有可能导致重复创建实例,当两个线程同时来调用MyClass.getInstance()的时候,由于myClass之前为null,所以两个线程会同时进入if条件内,创建两个实例。
解决多线程导致的问题就需要把getInstance()变成同步(synchronized)方法,代码如下
上面这种写法虽然解决了myClass被多次实例化问题,但是同步降低了性能。所以上面这种写法还是可以优化的。/** * 通过synchronized关键字,我们迫使所有的线程进入到这个方法之前,要等到别的线程离开该方法。 * @return */ public static synchronized MyClass getInstance(){ /** * 由于myClass是个静态变量,如果它为空表示该实例还没有被创建,我们就需要创建这个实例 * 如果myClass不为空,就表示该实例已经存在,直接return myClass; */ if(null==myClass){ myClass=new MyClass(); } return myClass; }
如何优化?上面的写法每一次访问getInstance都会进行一次同步,所以会降低性能,那我们可以思考一下是不是每次getInstance都有必要进行同步呢?肯定不需要,我们使用同步是为了防止myClass对象被多次实例化,只有在myClass为null的时候才会去实例化,所以我们可以先做一步判断myClass是否为空,然后再同步
public static MyClass getInstance() { /** * 由于myClass是个静态变量,如果它为空表示该实例还没有被创建,我们就需要创建这个实例 * 如果myClass不为空,就表示该实例已经存在,直接return myClass; */ if (null == myClass) { synchronized (MyClass.class) { if (null == myClass) { myClass = new MyClass(); } } } return myClass; }
最后贴上完整代码
/** * Created by xu on 2017/3/8. */ public class MyClass { /** * 利用静态变量来记录MyClass类的唯一实例 */ private static MyClass myClass; /** * 这里的构造函数私有化,防止别的类调用,只有内部才能调用来实例化 */ private MyClass() { } /** * 用getInstance()方法来实例化对象,并返回一个实例 * * @return */ public static MyClass getInstance() { /** * 由于myClass是个静态变量,如果它为空表示该实例还没有被创建,我们就需要创建这个实例 * 如果myClass不为空,就表示该实例已经存在,直接return myClass; */ if (null == myClass) { /** * 同步锁 */ synchronized (MyClass.class) { if (null == myClass) { myClass = new MyClass(); } } } return myClass; } }