码迷,mamicode.com
首页 > 其他好文 > 详细

设计模式-单例模式

时间:2015-05-24 08:57:01      阅读:116      评论:0      收藏:0      [点我收藏+]

标签:

单件模式 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;
	}
}


我们需要把singleton声明成 我们只需要把singleton声明成 volatile 就可以了这是因为:singleton = new Singleton()这句,这并非是一个原子操作,事实上在 JVM 中这句话大概做了下面 3 件事情。

  1. 给 singleton 分配内存
  2. 调用 Singleton 的构造函数来初始化成员变量,形成实例
  3. 将singleton对象指向分配的内存空间(执行完这步 singleton才是非 null 了)

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

volatile 关键字的作用就是确保当uniqueInstance变量被初始化成Singleton实例时,多个线程正确地处理uniqueInstance变量


设计模式-单例模式

标签:

原文地址:http://blog.csdn.net/u010786109/article/details/45937737

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!