首页 > 代码库 > [java]设计模式之singleton(单例)

[java]设计模式之singleton(单例)

在日常工作中,有很多对象,我们只需要一个。比如:线程池, 缓存,注册表等。如果制造出多个实例,就会导致许多问题,如程序行为异常,资源使用过量等。这就需要对对象的构建进行控制,使其只能产生一个对象。这就是本篇要讲的设计模式——singleton(单例)


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


那么,要如何实现单例模式,使得一个类只能产生一个对象呢?请看下面的实现:

public class Singleton {
	private static Singleton s;
	private Singleton() {
	}
	public static Singleton getInstance() {
		if (s == null) {
			s = new Singleton();
		}
		return s;
	}
}

你或许已经看懂了上面的代码。没错!我们可以通过将类的构造函数设成private访问权限,这样其他类就无法随意创建该类的对象了。然后我们应用一个静态的类对象来作为唯一的一个对象,并作为全局访问点。


clone()方法

还有一件事值得注意:类的clone()方法。例如, 如果基类实现了cloneable接口的话,子类就应该重写该方法。防止类的对象被copy,破坏单例特性。当然,在应用中应该灵活运用各种方法来防止clone()的各种情况。


多线程访问singleton方法

对于上面的实现方法,如果在多线程中被访问,可能会产生问题。

public class Singleton {
	private static Singleton s;
	private Singleton() {
	}
	// 如果多个线程同时访问, 有可能会出现多个实例。
	public static Singleton getInstance() {
		// 第一次初始化时,多个线程同时执行"if (s == null)",判断结果都为真,所以都会执行下面的操作:"s = new Singleton()",由此引发多个实例的出现。
		if (s == null) {
			s = new Singleton();
		}
		return s;
	}
}

那么该如何解决单例模式在多线程程序中的问题呢?

方案1:

将getInstance()变成同步(synchronized)方法,多线程的灾难可以轻易解决。

public class Singleton {
	private static Singleton s;
	private Singleton() {
	}
	
	public static <span style="color:#FF0000;">synchronized</span> Singleton getInstance() {
		if (s == null) {
			s = new Singleton();
		}
		return s;
	}
}

不推荐该方案! 因为对单例初始化只需要一次,这样做将会使得每次调用getInstance方法时都会进行同步。对运行效率是极大的累赘。


方案2:

在静态初始化时创建单例。

public class Singleton {
	// Early initialization.定义的时候就初始化。
	private static Singleton s = new Singleton();
	private Singleton() {
	}
	public static Singleton getInstance() {
		return s;
	}
}


该方法可以保证单例。但我们对程序设计时,通常保持直到使用时才创建对象的原则。


方案3:

用“双重检查加锁”,在getInstance()中减少使用同步。

public class Singleton {
	private static Singleton singleton;
	private Singleton() {
	}
	public static Singleton getInstance() {
		if (singleton == null) {
			synchronized (Singleton.class) {
				<span style="color:#FF0000;">if (singleton == null) { </span>  //必须
					singleton = new Singleton();
				}
			}
		}
		return singleton;
	}
}
这样将只有第一次创建对象时同步,既保证了线程安全,同时又保证了执行效率。

有人问为什么要在synchronized 内再一次判断if(sinlenton == null),我个人理解是这个原因:

如果两个线程都执行到了第一个if(singleton == null) 且都为true,这是总有一个线程先进入synchronized (Singleton.class)...部分代码,并阻塞另一个进入。当第一个进入后并成功建了一个实例singleton后,不再阻塞。另一个线程将进入执行synchronized (Singleton.class)...代码(因为它已经执行了第一个singleton==null 并为true)。如果里面不再进行判断,则会再一次创建一个singleton。
所以里面再一次判断了一次。


参考:

《head first 设计模式》

http://blog.csdn.net/natee/article/details/4408245#quote

[java]设计模式之singleton(单例)