HeadFirst 设计模式笔记(五)—— 单例模式

来源:互联网 发布:深圳加工中心编程招聘 编辑:程序博客网 时间:2024/06/07 07:04
singleton pattern
确保一个类只有一个实体,并提供一个全局访问点。这就是单例模式的功能。
典型的实现:
public class Singleton
{
private static Singleton uniqueS = null;
private Singleton(){} //将构造函数设为私有
public static Singleton getInstance()//用户必须使用此方法得到唯一的对象
{
if( uniqueS == null){uniqueS = new Singleton();}
else return uniqueS;
}
}
然而这种最基本的实现在使用多线程时可能出现同步问题,这一问题不是很容易察觉:如线程1执行完if( uniqueS == null)但是还没有执行实例化uniqueS;此时uniqueS仍为null,则线程2执行到if时也会进入实例化的程序块,如此一来我们就有了两个不同的对象!
有3种思路可以解决这一问题:
  1. 把getInstance变为同步方法,即加入synchronized关键字:public static synchronized Singleton getInstance();这样一来就不允许两个线程同时进入这一方法了。但实际上由于只有第一次调用时才需要同步,所以这种做法在之后的调用里变成了累赘,程序的执行效率可能下降100倍。
  2. 如果创建此实例的负担不是很重,那么可以急切(eagerly)创建此对象,而不是延迟创建。直接在明时创建:private static Singleton uniqueS = new Singleton();
  3. 双重加锁,只在当实例尚未创建时才进行同步。(java5以前的版本,volatile关键字会使双重加锁失效)
public class Singleton
{
private volatile static Singleton uniqueS = null;
private Singleton(){} //将构造函数设为私有
public static Singleton getInstance()//用户必须使用此方法得到唯一的对象
{
if( uniqueS == null)
{//只在当uniqueS未实例时才同步
synchronized(Singleton.class)
{
if( uniqueS == null)
uniqueS = new Singleton();
}
}
else return uniqueS;
}
}
关于volatile 关键字与synchronized关键字的区别:
线程安全涉及到两个方面,一个是程序的执行顺序问题,如上文所描述的问题,此时我们使用synchronized关键字来保证任何时候只有一个线程执行某段代码;另一个方面涉及到这一问题:变量的改变能否立刻影响到所有使用者。考虑到可能机器有多个cpu,每个cpu都有几层cache,每个线程操纵的都可能是一个拷贝,而不是变量的本体。使用volatile关键字可以强制线程每次在使用某一变量时都去读取最新的值,而不是使用缓存中的值。具体到例子中也就是,如果一个线程执行了uniqueS = new Singleton();那么在所有其他线程中uniqueS == null都立即为false。
原创粉丝点击