What
Singleton:保证一个类仅有一个实例,并提供一个訪问它的全局訪问点。
Why
Singletion是我比較熟悉的设计模式之中的一个,在寻常的开发过程中,也曾几次用到。它主要适用于例如以下场景:
1、当类仅仅能有一个实例并且客户能够从一个众所周知的訪问点訪问它时。
2、当这个唯一实例应该是通过子类可扩展的,而且客户应该无需更改代码就能使用一个扩展的实例时。
在系统设计中,在涉及系统资源的管理时,往往会被设计成Singletion模式,比方缓存、日志对象、线程池、对话框等等。
How
如果例如以下场景:一个简单的线程池。须要实现添加线程以及获取单个线程的功能。显然这个线程池对象是一个Singletion对象。
简单的实现代码例如以下:
public class ThreadPool {
private List<Runnable> threads=new ArrayList<Runnable>();
private static ThreadPool threadPool=null;
private ThreadPool(){
}
public static ThreadPool getInstance(){
if(threadPool==null){
threadPool=new ThreadPool();
}
return threadPool;
}
public void add(Runnable thread){
System.out.append("add a thread!");
threads.add(thread);
}
}
client调用
ThreadPool threadPool=ThreadPool.getInstance();
threadPool.add(new Thread());
以上代码类图例如以下:
Discuss
线程安全的Singleton实现
以上代码。实现的是一个学习意义上的版本号。在实际生产中,在一些情况下会出现故障。在多线程情况下,例如以下代码会出现什么问题?
public static ThreadPool getInstance(){
if(threadPool==null){
threadPool=new ThreadPool();
}
return threadPool;
}
在多线程条件下,当一个线程运行到new ThreadPool()可是还没返回给threadPool,这时thread=null,还有一个线程也会进入if代码片段。这样就创建了两个threadPool,造成这个的原因就是这段代码是线程非安全的。
经过优化形成例如以下版本号:
public static ThreadPool getInstance() {
synchronized (ThreadPool.class) {
if (threadPool == null) {
threadPool = new ThreadPool();
}
}
return threadPool;
}
这个版本号能够非常好的解决上个版本号的问题,在一个线程进入了synchronized代码块。还有一个线程就会等待。
可是细致想想。假设对象已经创建,线程还是须要等待进入synchronized代码块才会知道threadPool!=null。这样会造成比較严重性能问题。再来一个版本号
public static ThreadPool getInstance() {
if (threadPool == null) {
synchronized (ThreadPool.class) {
if (threadPool == null) {
threadPool = new ThreadPool();
}
}
}
return threadPool;
}
ok,这个版本号看上去完美了,能够在生产中使用了。这是线程安全的实现。
以上代码还是能够通过一些办法在一个JVM中创建多个ThreadPool实例。想想是什么?对,能够通过反射的方式来,创建n多个实例。java的反射机制能够通过private的构造器创建实例。
使用枚举实现Singleton
Effectvie java的作者Joshua Bloch提出了一个能够绝对防止多次实例化。并且无偿的提供了序列化机制的方法,使用枚举实现Singleton。当然java版本号须要在1.5以上。以下是以上演示样例的使用枚举的实现
public enum ThreadPool {
Instance;
private List<Runnable> threads=new ArrayList<Runnable>();
public void add(Runnable thread){
System.out.append("add a thread!");
threads.add(thread);
}
}
client调用
ThreadPool.Instance.add(new Thread());
能够看出使用枚举方式。代码比較简洁并且能够绝对防止多次实例化,是一个实现Singleton的很好的方法。