Java单列设计模式你用对了吗

来源:互联网 发布:h5打开淘宝app 编辑:程序博客网 时间:2024/05/29 16:29

这两篇文章写得太好了,突然不知道如何表达自己的心情了,就转载了一下,平时还真没有注意


http://blog.csdn.net/relicemxd/article/details/56894691


http://ifeve.com/double-checked-locking-with-delay-initialization/


作为程序猿这种特殊物种来说,都掌握了一种特殊能力就是编程思想,逻辑比较谨慎,但是有时候总会忽略到一些细节,比如我,一直以来总觉得Singleton是设计模式里最简单的,不用太在意,然而就是因为这种不在意在开发中吃亏了.真的too young to simple. 
好不扯淡了,直入主题.

在代码的世界里发现有各种写法的单例,有人说单例有5种,6种,7种… 
对于单例的分类这点必须规范下,首先这么多种的分类是根据什么来定义的,基准是什么?否则怎么会有那么多写法.

因此归纳下来,从延迟加载执行效率的角度出发主要也就分为两种,饿汉顾名思义就是执行效率高,但缺乏延时加载,其他写法差不多都是懒汉式的一个拓展,或者优化而演化出来的,下面请看代码.

开发中常用的单例-饿汉式

public class SingletonDemo1 {    private static final SingletonDemo1 s1 = new SingletonDemo1();    public static SingletonDemo1 getInstance() {        return s1;    }    private SingletonDemo1() {    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

没错上面这块代码叫做单例-饿汉式,饿汉式一直以效率高而闻名于单例界,因此咋们开发中常用的单例模式也会选择他,简单而好用. 
>

开发评价: ★★★★☆ 
延时加载: ★☆☆☆☆ 
执行效率: ★★★★★

耗时的蜗牛-懒汉式

public class SingletonDemo2 {    private static SingletonDemo2 s1;    public static synchronized SingletonDemo2 getInstance() {        if (s1 == null) {            s1 = new SingletonDemo2();        }        return s1;    }    private SingletonDemo2() {    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

hello world这个世界里都知道这种单例基本不会去用,在

double check双重检查锁-饿汉式

这可以说是上面饿汉式的一个缩影,为什么这么说,因为他并不完美,仍然有bug.

public class SingletonDemo3 {    private static SingletonDemo3 s1;    public static SingletonDemo3 getInstance() {        if (s1 == null) {        //这里使用了临时变量            SingletonDemo3 instance;            synchronized (SingletonDemo3.class) {                instance = s1;                if (instance == null) {                    synchronized (SingletonDemo3.class) {                        if (instance == null) {                            instance = new SingletonDemo3();                        }                    }                    s1 = instance;                }            }        }        return s1;    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

这个方式主要是通过if判断非null实例,提高了执行的效率,不必每次getInstace都要进行synchronize,只要第一次要同步,有没创建了不用.

但是为什么说这种写法有bug?这个问题主要是Java的jvm层内部模型引起的.简单点说就是instance引用的对象有可能还没有完成初始化完就直接返回该实例过去,在jdk1.5后这个问题才得到了优化,这不多说,可以看看这篇博文讲得不错. 
详情见

当然也有了一些解决方法

  • 使用volatile关键字解决双重检查锁定的bug,对于volatile关键字就是Java中提供的另一种解决可见性和有序性问题的方案.
public class SafeDoubleCheckedLocking {//添加了volatile关键字    private volatile static Instance instance;    public static Instance getInstance() {        if (instance == null) {            synchronized (SafeDoubleCheckedLocking.class) {                if (instance == null)                    instance = new Instance();//instance为volatile,现在没问题了            }        }        return instance;    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

>

开发评价: ★★★☆☆ 
延时加载: ★★★☆☆ 
执行效率: ★★★☆☆

推荐使用的静态内部类-饿汉式

public class SingletonDemo4 {    //通过静态内部类的方式来实例化对象    private static class InnerSingleton {        private static final SingletonDemo4 instance = new SingletonDemo4();    }    public static  SingletonDemo4 getInstance() {        return InnerSingleton.instance;    }    private SingletonDemo4() {    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

这周方式是利用了类加载的一些特性,在classloder的机制中,初始化instance时只有一个线程,而且这种方式还有一个好处就是起到了延时加载的效果,当SingletonDemo4被加载了,但是内部类InnerSingleton并不会被加载,因为InnerSingleton没有主动使用,只有通过调用getInstance方法时才会去加载InnerSingleton类,进而实例private static final SingletonDemo4 instance = new SingletonDemo4(); 
因此这种巧妙的方式比起双重检查锁来说,安全来又高效了些. 
>

开发评价: ★★★★☆ 
延时加载: ★★★★☆ 
执行效率: ★★★★☆

推荐使用的枚举-饿汉式

public enum SingletonDemo5 {    //枚举元素本身就是一个单例(名字可以随意定义);    INSTANCE;    //可以做一些单例的初始化操作    public void singletonOperation() {    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

这种方式其实很帅,但是在实际开发中很少人使用过,这点有点奇怪,首先enum本身就是一个单例,而且枚举还有一个特性,可以避免放序列化和反射破解单例问题,经理再也不用担心单例安全了,可能是1.5才有enum的原因吧,如果项目适合的话可以试下这种单例. 
>

开发评价: ★★★★☆ 
延时加载: ★★★★☆ 
执行效率: ★★★★☆

总结一下:

对于下面的单例总的来说各有各的优点,至于开发中使用哪个可以根据你的业务需求来选择.

  • 饿汉 
    • 标准饿汉 (安全防护方面 枚举单例更优于标准饿汉) 
      线程安全,高效,不可以懒加载
    • 枚举单例 
      线程安全,高效,不可以懒加载(天然避免反射与反序列化) 
  • 懒汉 (效率方面 静态内部类更优于标准懒汉) 
    • 标准懒汉 
      线程安全,低效,可以懒加载
    • 双重检测(不推荐,有bug) 
      线程安全,低效,可以懒加载
    • 静态内部类 
      线程安全,低效,可以懒加载 

0 0
原创粉丝点击