设计模式(单例模式)

来源:互联网 发布:java web开发实战 编辑:程序博客网 时间:2024/06/08 08:55

单例模式

在决定写设计模式系列博客前我慎重的考虑了一番,首先网上关于设计模式的文章实在是太多了,很难写出什么特色,再者我觉得自己水平比较有限不知道能不能写好。可是思虑了很久我还是决定写一个设计模式的系列,别人写的东西也许能看懂但不一定就真的会了,就算是会了也不一定就能正确的使用,同是打狗棒法,洪七使出来和丐帮的弟子使出来那完全不是一样的境界和效果,所以我决定自己搞一遍,加深理解。

Design.Patterns中单例的定义:

Ensure a class only has one instance, and provide a global point of access to it.
保证一个类仅有一个实例,并提供一个访问它的全局访问点。

学习单例模式的时候我一直在想,为什么要搞出来一个单例模式呢?想来想去发现这么搞还是有些道理的,面向对象本质上是对现实世界的模拟,而现实世界中有些东西确实是不能有两个或多个的,或者在一定的范围内有些东西是不能有多个的。自古就有一山不容二虎之说,设计模式之禅中举了一个很好的例子,那就是一个国家只能有一个皇帝,不能有多个,一旦有多个皇帝那肯定是要打仗的。在程序中也一样,有些类是不能有多个实例的,比如统计网站的访问次数,你能用多个对象去统计吗?再比如电脑中的任务管理器,你能搞多个任务管理器来管理这些进程吗?再比如程序日志的追加,你能搞多个对象去同一个文件中追加日志信息吗?要是非要较真我觉得技术上肯定有办法实现的,但是搞起来怕是会很费劲的。我们可以用生活的角度来思考程序,有些事情就是一个人干的,你安排两个人肯定会有问题的,程序也是一样,有些事情就是只能由一个实例完成的就不要搞多个实例。单例模式就是在你需要用到这个实例的时候给你提供这个实例,而且不论谁用我都给你的是同一个实例。

请看下面代码

public class Singleton {}
public class SingletonProvide {    private static final Singleton singleton = new Singleton();    public static Singleton getInstance() {        return singleton;    }}

我们的目的就是在程序的任何地方使用同一个Singleton对象,从功能上看这段代码完全满足了我们的使用需求,没有问题吧?功能上我觉得是没有问题,但是开发中就有问题了,开发往往是一个团队,如果某个人不知道Singleton是单例的或者他知道但是就是想搞点破坏,他自己创建了一个Singleton或者SingletonProvide,那就不能保证程序中只有该类的一个实例了。因此代码就要写成下面的样子。

单例实现一(饿汉式线程安全)

public class Singleton {    private static final Singleton singleton = new Singleton();    // 构造函数私有让想搞破坏的人无法创建对象    private Singleton() {    }    public static Singleton getInstance() {        return singleton;    }}

这个就是比较中规中矩的单例模式的实现了,设计模式之禅中也比较推荐这种实现方式,因为singleton是静态的不用考虑多线程问题。缺点就是类加载时就要初始化,比较浪费内存,当然了,都什么年代了,电脑内存那么大跑那么快,浪费一点也没关系,可以这么用的。

单例实现二(懒汉式非线程安全)

public class Singleton {    private static Singleton singleton;    // 构造函数私有不让想搞破坏的人创建对象    private Singleton() {    }    public static Singleton getInstance() {        if (singleton == null) {            singleton = new Singleton();        }        return singleton;    }}

这种实现在单线程下是没有问题的,但是在多线程下就可能创建多个实例对象,解决多线程也比较简单同步一下就好了,代码如下。

单例实现三(懒汉式非线程安全)

public class Singleton {    private static Singleton singleton;    // 构造函数私有不让想搞破坏的人创建对象    private Singleton() {    }    // 对获取实例的方法进行线程同步    public static synchronized Singleton getInstance() {        if (singleton == null) {            singleton = new Singleton();        }        return singleton;    }}

但是对一个方法说同步就同步了,是不是太草率了?synchronized的作用范围内代码是越少越好,所以我们把创建过程进行同步就行了,下面就是双层查锁的单例实现。

单例实现四(双层查锁线程安全)

public class Singleton {    private volatile static Singleton singleton;    // 构造函数私有化,不让外部创建对象    private Singleton() {    }    // 双层查锁    public static Singleton getInstance() {        if (singleton == null) {            synchronized (Singleton.class) {                if (singleton == null) {                    singleton = new Singleton();                }            }        }        return singleton;    }}

细心的同学会发现多了一个volatile关键字,这玩意牵扯的东西太多了,我不想讨论这玩意,这么用就行了,当然了,不推荐这么玩。

单例实现五(Holder式线程安全)

这个是我比较推荐的,设计的很巧妙

/** * 用抽象类防止通过反射创建类的实例 * @author liujh_2017-10-21 */public abstract class Singleton {    // 内部类提供实例,完成了懒加载    private static class PrinterHolder {        private static final Singleton INSTANCE = new Singleton() {        };    }    // 获取类的实例    public static Singleton getInstance() {        return PrinterHolder.INSTANCE;    }}

常见的单例模式的实现大概就是这些,还可以通过枚举来实现单例,我也试了一下,但是我这里不想对这种实现方式进行讨论,枚举我没怎么用过,怕误导大家。

参考资料:
1.设计模式之禅
2.Design.Patterns.Elements.Of.Reusable.Object.Oriented.Software
3.Design.Patterns中文版
4.Effective Java(第2版)

原创粉丝点击