设计模式之我见-------单例模式

来源:互联网 发布:英本网linux内核视频 编辑:程序博客网 时间:2024/05/17 20:53

说到单例模式,大家肯定不陌生,即时有的没有听说过,但是项目中也可能用到过,只是不知道它的名字罢了。
单例模式,顾名思义,在系统中有一个类只有一个实例而且该实例易于外界访问,从而方便对实例个数的控制并节约系统资源。
下面举个大家在实际中常用的例子

public class SingleTon {    public static SingleTon singleTon = null;    private static SingleTon getInstance(){        if(singleTon == null){            singleTon = new SingleTon();        }        return singleTon;    }}

上面是我常用的单例模式,这种模式一般称为懒汉模式。

  • 懒汉模式
    顾名思义,是一个懒加载的模式,从上面的代码中可以看出,我先创建一个空的静态成员,但是不实例化它,等到外界调用getInstance时,我才实例化它,然后调用该类的方法。就像一个懒汉,懒得不想吃饭,只有别人喊他一声吃饭了,他才吃一口,不叫他的话就一直这么饿着。
    但是这种写法也有问题,当多线程调用它时,这里就会出问题了,例如有两个线程A,B,线程A先进来,判断singleTon我空,进去里面实例化,恰好此时,线程B也进来了,然后它也实例化了一个singTon,这样,在单例模式下就创建了两个实例,违反了单例模式的规则。所以为了防止这种情况出现,就要给这个模式加上一个线程锁synchronized。
    public static SingleTon getInstance(){        if(singleTon == null){           synchronized (singleTon){               if(singleTon == null){                   singleTon = new SingleTon();               }           }        }        return singleTon;    }

上面的代码可以看到,当线程A发出请求后,会先检查singleTon是否为null,如果不是则直接返回其内容,这样避免了进入synchronized块所需要花费的资源。当如我上面所讲的那样,两个线程同时都对singleTon进行为空判断然后都进来了之后,那么他们也必须按照顺序执行synchronized块中的代码,第一个进入代码块的线程会创建一个新的Singleton实例,而后续的线程则因为无法通过if判断,而不会创建多余的实例。
其实写到这里,这种单例模式的写法还是有一些问题的,在JDK1.5之前JMM(Java Memory Medel)中Cache、寄存器到主内存回写顺序的规定,初始化构造器和给singleTon分配内存对象时的顺序不能保证是按顺序进行,所以在JDK1.5以后,private volatile static SingletonKerriganD singleTon= null这种写法更为规范,但是在1.42以前的版本,这种写法还是会有几率报错的。

  • 饿汉模式
    嗯~先上例子:
public class SingleTon {    public static SingleTon singleTon = new SingleTon();    public static SingleTon getInstance(){        return  singleTon;    }}

在这个模式中,先将singTon实例化,等想要用它的时候直接返回就可以了,这就是简单的饿汉模式,很形象,就像一个饿汉,肚子饿了,但是没到饭点(别人调用它),他先吃一块面包顶顶饿,这块面包就是先实例化的singTon。
这种写法同时还保证了线程安全,无论哪个线程调用它,返回的都是事先早已实例化好的那个singTon,但是这种写法这么好,为什么还要上面那种懒汉模式的那么多代码呢?原因就是占用资源了,不管你用不用这个singleTon,他先搞出来再说。如果是懒汉是时间换空间的话,饿汉就是空间换时间,每次调用的时候,就不需要再判断了,节省了运行时间,牺牲了资源空间。
下面是对饿汉模式的进一步优化:

 */public class SingleTon {    private static class mySingleTon {        static final SingleTon singleTon = new SingleTon();    }    public static SingleTon getInstance() {        return mySingleTon.singleTon;    }}

这种写法由于mySingleTon是私有方法,除了getInstance()之外没有办法访问它,因此它是懒汉式的;同时多线程获取实例的时候不会发生同步,这个也解决了懒汉模式中的JDK版本问题。这种静态内部类写法可以延迟进行加载,减少内存开销。因为用到的时候才加载它,比上面的简单写法优点还是有的。

  • 枚举模式
    最后这种模式我看到百度上或者博客上有很多人在推荐。
public enum SingleTonEnum {    itSelf;  //定义一个枚举的元素,就代表SingleTonEnum的一个实例    SingleTonEnum() {    }    public SingleTonEnum getInstance() {      return itSelf;    }}

这个我其实没怎么用过,我看了一些百度上博客的人对这种写法好处的看法,并总结了一下:
1.枚举写法简单
2.枚举能自己处理序列化(这个我还不太了解)和保证自身的线程安全。
3.枚举能防止反射攻击,上面那种饿汉的简单模式写法就不能防止反射攻击。
最后总结下,我们了解了单例模式各种实现方案的优缺点,但是这些构造模型,并没有好坏之分,只有根据你所处的上下文环境,才能灵活的应用各种模式,所以,理解原理才是王道,模板什么的都是浮云。

0 0
原创粉丝点击