设计模式-单例模式

单件模式 Singleton Pattern

  类只存在一个实例,即只可以创建一个对象。有一些类如果创造出多个对象就会导致许多问题的产生,如程序的行为异常、资源使用过量,或者是不一致的结果。单件模式常常被用来管理共享的资源,例如数据库连接或者线程池。

  简要定义:单件模式确保一个类只有一个实例,并提供一个全局访问点

单件模式的实现

  单件模式的实现是通过private构造函数,类中含有一个静态方法getInstance(),调用这个方法可以创建并返回唯一的对象,也可能返回的是已经创建好的对象。

  分析:单件模式,首先要限制从外部创建对象,所以将构造方法声明为私有private,这样就只能在类内部调用构造函数。如何调用这个构造函数?要使用这个类,而又没有这个类的对象,所以需要一个静态的方法,这个方法调用私有的构造函数,并且返回唯一的单件对象。

剖析经典单件模式的实现

下面是经典单件模式的实现: 

public class Singleton {private static Singleton singleton = null;private Singleton() { }public static Singleton getInstance() {if(singleton== null) {singleton=newSingleton();}return singleton;}}这种实现方式存在问题,在多线程的情况下,可能会创建多个对象。

我们可以通过增加synchionized关键字到getInstance方法中,迫使每个线程在进入这个方法之前,要先等候别的进程离开该方法。

public class Singleton {private static Singleton singleton = null;private Singleton() { }public static synchionized Singleton getInstance() {if(singleton== null) {singleton=newSingleton();}return singleton;}这样的同步可能会造成性能问题,改善途径有两种:

1.使用“急切”创建实例,而不用延迟实例化的做法:

public class Singleton {private static Singleton uniqueInstance = new Singleton(); private Singleton() {} public static Singleton getInstance() {return uniqueInstance;}}利用这个做法,我们依赖JVM在加载在加载这个类时马上创建唯一的单件实例.JVM保证在任何线程访问uniqueInstance静态变量之前,一定先创建此实例

如果我们希望他能在我第一次getInstance()时才被真正的创建。这样,我们可以控制真正的类创建的时刻,而不是把类的创建委托给了类装载器。可以用如下版本实现:

public class Singleton {private static class SingletonHolder {private static final Singleton INSTANCE = newSingleton();}privateSingleton (){}public static final Singleton getInstance() {return SingletonHolder.INSTANCE;}}上面这种方式,仍然使用JVM本身机制保证了线程安全问题;由于 SingletonHolder 是私有的,除了 getInstance() 之外没有办法访问它,因此它只有在getInstance()被调用时才会真正创建;同时读取实例的时候不会进行同步,没有性能缺陷;也不依赖 JDK 版本。

2.使用"双重检查加锁“,在getInstance中减少使用同步:

首先检查是否实例已经创建了,如果未创建,才进行同步,这样一来,只有第一次会同步。

public class Singleton {private volatile static Singleton uniqueInstance; private Singleton() {} public static Singleton getInstance() {if (uniqueInstance == null) {synchronized (Singleton.class) {if (uniqueInstance == null) {uniqueInstance = new Singleton();}}}return uniqueInstance;}}<span style="font-size:18px;"><strong>volatile</strong>关键字的作用就是确保当uniqueInstance变量被初始化成Singleton实例时,多个线程正确地处理uniqueInstance变量</span>需要把singleton声明成成volatile 这是因为:singleton= new Singleton()这句,这并非是一个原子操作,事实上在 JVM 中这句话大概做了下面 3 件事情。

但是在 JVM 的即时编译器中存在指令重排序的优化。也就是说上面的第二步和第三步的顺序是不能保证的,最终的执行顺序可能是 1-2-3 也可能是 1-3-2。如果是后者,,则在 3 执行完毕、2 未执行之前,被线程二抢占了,这时 instance 已经是非 null 了(但却没有初始化),所以线程二会直接返回 instance,然后使用,然后顺理成章地报错。

知已知彼,百战百胜。

设计模式-单例模式

相关文章:

你感兴趣的文章:

标签云: