设计模式-创建型之单例模式

来源:互联网 发布:新浪微博怎么修改域名 编辑:程序博客网 时间:2024/05/29 15:39

模式动机

       在软件开发中,对于某些特定的类来说,只存在一个实例尤为重要,比如一个系统中的ID生成器。但如何才能保证这个类只有一个实例同时又容易被访问呢?一个比较好的解决办法是让这个类自身负责创建和保存它的唯一实例,并且它需要提供一个方法,用于获取该唯一实例。

模式定义

       一个类只有一个实例,这个类在自身内部进行实例化,并把此唯一实例提供给整个系统,即提供一个该实例的访问方法。这个类就叫单例类,这种模式就就单例模式。单例模式也叫单件模式或者单态模式。单例模式主要有三个要点:

  • 某个类只能有一个实例;
  • 自行创建该实例;
  • 提供访问该实例的方法。

模式结构

单例模式
       单例类拥有一个私有的构造方法,这样可以防止其他类通过new关键字来进行实例化它,同时,还包含一个静态私有成员变量(instance)和公有的静态方法(getInstance)来负责提供访问该唯一实例的入口。

单例模式的几种实现方式

饿汉式

 public class Singleton {    private static Singleton instance = new Singleton();    private Singleton(){        System.out.println("singleton init...");    }    public static Singleton getInstance(){        return instance;    }}

       这是单例模式最简单的实现形式,在内部实例化一个私有成员变量instance,其他的类通过公有静态方法getInstance()来获取该实例。至于为什么叫饿汉式,可以理解为该单例类比较“饿”,所以不管三七二十一先new一个实例出来再说。

饱汉式

       在饿汉式单例模式中,由于instance是static静态变量,所以当JVM一加载此类时,该单例对象(instance)就会被创建,这样其实是不好的,假设该单例创建的过程非常耗时,而这个单例类大部分情况下是不会用到的,那么这样就是一种很不好的实现方式。我们更希望的是当用到这个单例类实例时,才去创建它,即达到延迟加载的效果。实现方式如下:

public class LazySingleton {    private static LazySingleton instance = null;    private LazySingleton(){        System.out.println("singleton init...");    }    public static synchronized LazySingleton getInstance(){        if(instance == null){            instance =  new LazySingleton();        }        return instance;    }}

       在饱汉式单例模式中,我们先将instance设置为null,然后在getInstance中判断instance是否为null,如果不为null,则实例化该对象,然后返回。这样就实现了延迟加载,当真正需要用到instance时才会去创建。同时要注意,getInstance此时必须是同步的,需要synchronized,否则在多线程环境下,第一个线程正在创建单例,在创建的过程中,第二个线程进来了,此时instance依旧是null,然后第二个线程也进入了if语句块,这样就可能会导致多个实例产生。为什么这个要叫饱汉式呢,可以理解为程序很“饱”,不需要一来就创建,所以创建之前先判断一下是否为null,为null的时候再创建。

内部类式

       在饱汉式单例模式中,由于我们加上了同步关键字synchronized,显然会损失掉一定的性能,那么还有没有更好的方式呢?我们可以继续改进,使用内部类的方式,如下:

public class InnerSingleton {    private InnerSingleton(){        System.out.println("singleton init...");    }    private static class SingletonHolder{        private static InnerSingleton instance = new InnerSingleton();    }    public static InnerSingleton getInstance(){        return SingletonHolder.instance;    }}

       在这种实现中,当InnerSingleton 被加载时,其内部类SingletonHolder不会被初始化,而当getInstance被调用时,才会加载SingletonHolder,从而初始化单例类InnerSingleton。这样即可以实现延迟加载,同时也解决了多线程同步的问题。

适用场景

  • 系统只需要一个实例对象
  • 客户端调用该实例只允许使用一个公共访问点

总结

       单例模式是较为简单的设计模式,但也是很常见也很常用的一种模式。一共有饱汉式、饿汉式、内部类这三种实现方式,我们最佳的实现方式就是内部类式,既实现了延迟加载,同时也支持多线程,所以我们会使用这一种其实就够了,饱汉式、饿汉式有个印象即可。

0 0