设计模式之单例模式

来源:互联网 发布:vba提取网页数据 编辑:程序博客网 时间:2024/04/28 08:30
定义:
确保一个类只有一个实例,并提供一个全局访问点。

简介:
单例模式是设计模式中最简单的形式之一。这一模式的目的是使得类的一个对象成为系统中的唯一实例。要实现这一点,可以从对其进行实例化开始。因此需要用一种只允许生成对象类的唯一实例的机制,阻止所有想要生成对象的访问。使用工厂方法来限制实例化过程。这个方法应该是静态方法(类方法),因为让类的实例去生成另一个唯一实例毫无意义。

首先,我们来看一下经典单例模式的实现。
package net.csdn.blog.ruancoder;public class Singleton {    // 利用一个静态变量来记录Singleton类的唯一实例    private static Singleton instance;    // 这里可以添加其他有用的变量    // 把构造器声明为私有的,只有在Singleton类内才可以调用构造器    private Singleton() {    }    // 利用getInstance()方法实例化对象,并返回这个实例    public static Singleton getInstance() {        // 如果instance是空的,表示还没有创建实例        if (instance == null) {            // 利用私有的构造器产生一个Singleton实例并赋值给instance静态变量            // 如果我们不进入该方法,它就永远不会产生            instance = new Singleton();        }        // 当执行到这个return,就表示我们已经有了实例,并将instance返回        return instance;    }    // 这里可以添加其他有用的方法}

如果现在有两个线程都要执行getInstance()方法,可能会出现如下情况。

从图中可以看到,这里生成了两个对象,违反了单个实例的原则。

那么如何解决呢?只需要把getInstance()方法变成同步(syncronized)方法,多线程灾难几乎就可以轻易地解决了。


同步getInstance()方法

package net.csdn.blog.ruancoder;public class Singleton {    private static Singleton instance;    private Singleton() {    }    // 通过增加synchronized关键字到getInstance()方法中,我们迫使每个线程在进入这个方法之前,要先等候别的线程离开该方法    // 也就是说,不会有两个线程可以同时进入这个方法    public static synchronized Singleton getInstance() {        if (instance == null) {            instance = new Singleton();        }        return instance;    }}

这种实现在解决问题的同时,会带来性能的降低。只有第一次执行此方法,才真正需要同步。换句话说,一旦设置了instance变量,就不再需要同步这个方法了。之后每次调用这个方法,同步都是一种累赘。


如果getInstance()的性能对应用程序不是很关键,就可以使用同步getInstance()的方式,既简单又高效。


急切实例化

为了避免同步带来的性能降低,还可以使用急切创建实例,而不用延迟实例化的做法。
package net.csdn.blog.ruancoder;public class Singleton {    // 在静态初始化器中创建单利,保证了线程安全    private static Singleton instance = new Singleton();    private Singleton() {    }    public static Singleton getInstance() {        // 已经有实例了,直接返回        return instance;    }}

利用这个做法,我们依赖JVM在加载这个类时马上创建此类唯一的实例。JVM保证在任何线程访问instance静态变量之前,一定会先创建此实例。

如果应用程序总是创建并使用单例,或者在创建和运行方面的负担不太繁重,可以使用该方式。


双重检查加锁

我们也可以使用双重检查加锁,在getInstance()中减少使用同步。
package net.csdn.blog.ruancoder;public class Singleton {    // volatile关键字,确保当instance变量被初始化成Singleton实例时,多个线程正确地处理instance变量    private volatile static Singleton instance;    private Singleton() {    }    public static Singleton getInstance() {        // 检查实例,如果不存在,就进入同步区块        if (instance == null) {            // 注意,只有第一次才彻底执行这里的代码            synchronized (Singleton.class) {                // 进入区块后,再检查一次,如果仍是空,才创建实例                if (instance == null) {                    instance = new Singleton();                }            }        }        return instance;    }}

利用双重检查加锁,首先检查实例是否已经创建了,如果尚未创建,才进行同步。这样一来,只有第一次会同步,这正是我们想要的。

如果性能是我们关心的重点,那么这个做法可以大大地减少getInstance()的时间消耗。


最后总结一下单例模式的要点:
1.单例模式确保程序中一个类最多只有一个实例。
2.单例模式也提供访问这个实例的全局点。
3.在Java中实现单例模式需要私有的构造器、一个静态方法和一个静态变量。
4.确定在性能和资源上的限制,然后小心的选择适当的方案来实现单例,以解决多线程的问题。
5.如果使用多个类加载器,可能导致单例失效而产生多个实例。
0 0
原创粉丝点击