设计模式之单例模式

来源:互联网 发布:数据找规律 编辑:程序博客网 时间:2024/06/02 05:48

设计模式之单例模式


单例模式

“单例(Singleton)”简单理解就是单一实例,全局最多只能创建一个该类的实例对象,该类提供得到该实例的全局访问点,通常是getInstance()这个静态方法。可能你会疑问了,那我想得到对象,我new不就行了?对不起,这个类的构造方法修饰符是private,没给你new的机会啊。那好吧。。。我们来看一下最简单的例子:

public class Singleton {    private static Singleton mInstance; // Singleton模式全局唯一的实例对象    private static int count = 0;       // 代表当前类被实例化的次数    //私有构造方法,让你new无可new    private Singleton() {        count++;        System.out.println("啊,被实例化了" + count);    }    /**     * 全局提供的静态方法访问点     * @return 全局唯一的Singleton实例对象     */    public static Singleton getInstance() {        if (mInstance == null) {            mInstance = new Singleton();        }        return mInstance;    }    /**     * 打印一下当前对象的hashcode     */    public void printHashCode() {        System.out.println("hashCode--->" + this.hashCode());    }}

接下来,看看测试类:

public class Test {    public static void main(String[] args) {//      Singleton singleton = new Singleton();  //这个肯定报错的        Singleton st1 = Singleton.getInstance(); //这个嘛就是我们想要的了        st1.printHashCode();        Singleton st2 = Singleton.getInstance(); //再来一个        st2.printHashCode();     }}

你猜结果:

啊,被实例化了1hashCode--->366712642hashCode--->366712642

没有出乎你的想象吧,这两个实例的hashcode是相同的,而且构造方法只被调用了一次。注意点:
1. getInstance()这个方法必须是静态的;
2. 内部的对象mInstance也必须是静态的,不然静态方法怎么访问非静态成员呢?
3. 构造方法必须是private修饰的,不然可以被各种new,全局可就有数不清的Singleton对象了(好奇怪)。


你以为这样就完了?好吧,单线程中,这样确实差不多了哈,但是一遇到多线程,你可就要仔细仔细考虑了,来看个示意图吧

public static Singleton getInstance() {        if (mInstance == null) { //①            mInstance = new Singleton();//②        }        return mInstance;    }

当两个线程同时执行到①这儿,是不是就有机会创造不同的对象了呢?答案是肯定的啊!
我们把测试类中主方法内容改一改:

public static void main(String[] args) {    Runnable r = new Runnable() {        @Override        public void run() {            Singleton st = Singleton.getInstance();            st.printHashCode();        }    };    new Thread(r).start();//来一个新线程    new Thread(r).start();//再来一个新线程    //你还可以多来几个新线程,看看结果乱成什么样子,一定不会让你失望的}

看到结果,我是平静的,完全意料之中

啊,被实例化了2啊,被实例化了2hashCode--->358411109hashCode--->1980094282

那该怎么解决呢?想必你已经想到了,线程同步加synchronized关键字,那我们不妨试试,public synchronized static Singleton getInstance()仅仅多加了这个关键字,其他啥都变,再试试,结果一定是您期望的那样,这儿就不贴出来了,就让客官自己动手写写看效果吧。


扩展
谈到这儿,基本概念也应该知道了吧,那我们再扩展扩展呗,把传说中的“饿汉模式”、“懒汉模式”说说呗。
这两种都是单例模式,只是具体实现方式不同。

懒汉模式

其实前面写的代码就是懒汉模式,说他懒,是因为:每次你调用getInstance()方法的时候,它才会去判断该唯一实例对象是否存在,不存在则new出来返回给你。

饿汉模式

说它‘饿’是因为它真的很饿,你饿了吗?我好像有点饿了。。。好吧,不知道说到哪儿去了。饿汉预先把对象new出来(就像人,饿了,先吃饱,好像比喻不怎么恰当,没关系啦)。上代码吧

public class LazySingleton {    //类加载的时候,就预先new出来    private static LazySingleton mInstance = new LazySingleton();    private static int count = 0;    private LazySingleton() {        System.out.println("count--->" + (++count));    }    //直接返回对象    public static LazySingleton getInstance() {        return mInstance;    }    public void printHashCode() {        System.out.println("hashcode--->" + this.hashCode());    }}

这个好处是:即使多线程,也不用考虑同步的问题,因为类加载的时候就创建了唯一的对象,要得到实例时,直接返回即可。


好吧,看似很简单的单例模式,其实也并不简单,对于饿汉模式,还有一些可以优化的地方,采用“双重检查加锁”,在getInstance()中减少使用同步 。 具体请参考下面:

《Head First 设计模式(中文版)》 page182


题外话
这一段时间,一直在忙另外一个项目,所以没有更新,不过,以后打算还是要坚持规律的更新一下吧。MarkDown编辑器还不是很熟,慢慢用着用着就好了吧,毕竟以前也没用过,也没花时间去学,我承认我有点懒了。
这篇没有讲故事,因为单身和单例就比较配哦,没有故事可讲,嘻嘻。

原创粉丝点击