为什么需要单件模式
我们为什么要用到单件模式,当我们用到这些对象如:线程池,缓存,注册表和日志对象等,事实上,这些对象我们只能有一个实例,不然会导致很多问题出现,所以我们要将它弄成单件的。
分析常用单件模式
类图很简单
package headfirst.hd.singleton;
public class Singleton {
static Singleton uniqueInstance;
private Singleton() {} //不允许通过new方式得到实例化对象
//获取对象唯一方式
static Singleton getInstance() {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
}
测试类TestSingleton
package headfirst.hd.singleton;
public class TestSingleton {
public static void main(String[] args) {
Singleton singleton = Singleton.getInstance();
Singleton singleton2 = Singleton.getInstance();
System.out.println(singleton == singleton2);
System.out.println(singleton.equals(singleton2));
}
}
在一个单线程中运行,这样已经完美了,但如果有很多个线程并发的情况下,那单件可能就不是单件了。
具体分析如下图
多线程下几种单件模式
将方法同步,加入synchronized
将getInstance方法封装成原子性操作
package headfirst.hd.singleton;
public class SingletonSyn {
static SingletonSyn uniqueInstance;
private SingletonSyn() {}
//将getInstance方法封装成原子性操作
static synchronized SingletonSyn getInstance() {
if (uniqueInstance == null) {
uniqueInstance = new SingletonSyn();
}
return uniqueInstance;
}
}
虽然功能上解决了以上问题,但是每次获取单件时,都会加锁,使得其成为累赘,由于上图我们分析得知,只有第一次获取单件时候,才会造成多线程bug,所有我们改善一下哈
急切式
package headfirst.hd.singleton;
public class Singleton {
static Singleton uniqueInstance = new Singleton();
private Singleton() {}
static Singleton getInstance() {
return uniqueInstance;
}
}
双重检查加锁
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;
}
}
为什么要加关键字volatile
参考:http://blog.csdn.net/dengjili/article/details/79464014
将类改造为单件模式案例
主要分以下步骤(使用急切式,毕竟简单)
- 【第一步】将构造方法改为私有方法
- 【第二步】定义静态变量,并实例化变量
- 【第三步】提供唯一获取单件的方法
案例
原来类ChocolateBolier
package headfirst.hd.singleton;
public class ChocolateBolier {
private boolean empty;
private boolean boiled;
public ChocolateBolier() {
this.empty = true;
this.boiled= false;
}
public void fill() {
if (isEmpty()) {
empty = false;
boiled = false;
//填充巧克力和牛奶的混合物
}
}
public void drain() {
if (!isEmpty() && isBoiled()) {
empty = true;
//排除煮熟的巧克力和牛奶的混合物
}
}
public void boil() {
if (!isEmpty() && !isBoiled()) {
boiled = true;
//排除煮熟的巧克力和牛奶的混合物
}
}
public boolean isEmpty() {
return empty;
}
public boolean isBoiled() {
return boiled;
}
}
将类ChocolateBolier改造为单件
package headfirst.hd.singleton;
//使用急切式,毕竟简单
public class ChocolateBolier {
//【第二步】定义静态变量,并实例化变量
static ChocolateBolier instance = new ChocolateBolier();
private boolean empty;
private boolean boiled;
//【第一步】将构造方法改为私有方法
private ChocolateBolier() {
this.empty = true;
this.boiled= false;
}
//【第三步】提供唯一获取单件的方法
static ChocolateBolier getInstance() {
return instance;
}
public void fill() {
if (isEmpty()) {
empty = false;
boiled = false;
//填充巧克力和牛奶的混合物
}
}
public void drain() {
if (!isEmpty() && isBoiled()) {
empty = true;
//排除煮熟的巧克力和牛奶的混合物
}
}
public void boil() {
if (!isEmpty() && !isBoiled()) {
boiled = true;
//排除煮熟的巧克力和牛奶的混合物
}
}
public boolean isEmpty() {
return empty;
}
public boolean isBoiled() {
return boiled;
}
}