The Singleton Pattern
2019-02-11 本文已影响0人
想跳舞的兔子
本篇我们要讲的是单例模式(the Singleton Pattern)
单例模式是旨在创造只被创建一次的对象。
1.模式起源:
首先,让我们探讨以下,该模式是在何种需求下产生的。
当我们需要调用对象时,我们通常会创建一个来使用,但是,对于那些共享的变量,我们不能随意创建,因为多例会使本身应该只有一份的对象变成多份,弄乱程序,我们需要做的努力是,如何获取调用一个对象,且该对象只被创建一次。
2.模式代码进阶:
首先,我们只对于单线程进行思考。
1.不能被随意创建的对象意味着我们需要把public的构造器变成private。
2.因为只有静态类变量/函数可以在不初始化类的条件下依旧可以被调用,所以,我们需要static
3.为了被私有构造函数创建的单个对象被引用,我们需要定义一个static 的类变量,一个调用对象的static类函数.
综上,我们可以得出第一个singleton pattern
public class Singleton{
private static Singleton uniqueInstance;
//other useful instance variables here
private Singleton(){}
public static Singleton getInstance(){
if (uniqueInstance == null)
{ uniqueInstance = new Singleton();}
return uniqueInstance;
}
//other useful methods here
}
当上例纯粹单线程的时候是没有问题的,但是如果在多线程环境,上述代码会出问题。假设,我们有两个线程同时进行到if (uniqueInstance == null) 且当前uniqueInstance确实为null,那么这两个线程会分别创建一个对象,这就会出问题了。为了修正多线程下的这个问题,我们引入synchronized.
public class Singleton{
private static Singleton uniqueInstance;
//other useful instance variables here
private Singleton(){}
public static synchronized Singleton getInstance(){
if(uniqueInstance == null)
{ uniqueInstance = new Singleton();}
return uniqueInstance;
}
}
引入动态synchronized的代价比较大,我们从下面几个方面来改进多线程:
- 如果getInstance()的性能并不是至关重要的,那么采用synchronized即直观又有效。但是当getInstance的性能很重要的时候,你需要重新考虑
- 如果单例变量并不是特别占用空间和时间,而且在程序中一定会创建,那么,我们可以提前创建好,这样避免了多线程可能的重复创建。
public class Singleton{
private static Singleton uniqueInstance = new Singleton();
private Singleton(){}
public static Singleton getInstance()
{ return uniqueInstance;}
}
+我们也可以采用“double-checked locking”来替换synchronized在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;
}
}
注意:该“double-checked locking”在java版本低于1.4上是不好用的。在JVM上有其他方式来保证同步性,如果你使用JVM而不是java5的话,需要采用其他方式来处理。