链接地址:http://www.xx566.com/detail/112.html
前段时间在读Effective Java的时候,对单例(Singleton)模式的三种实现方式做过一点总结,不过当时也是一知半解,对其内部以及为什么这么设计并没有深入的理解,最 近在工作中又接触到了单例模式,有空又翻阅了一些设计模式的书籍,于是想重新做一下整理和理解。
单例模式可能是众多设计模式中,类设计最简单的模式,不过它的作用却非常重要,在实现上也要许多需要注意的地方,首先, 单例模式的实现目前来看有三种方式:饿汉式、懒汉式和枚举式(具体内容请点击:三种方式实现类的Singleton(单例)),不过上次总结的时候,没有深入的研究,也没有考虑多线程并发之类的问题,这次在总结过程中,会特别注意一下。
以下是实现单例的三种方式,我们来看一下,饿汉式单例实现,代码如下:
package javase.singleton;
/**
* 饿汉式单例
*
* @author Realfighter
*
*/
public class SingletonStarving {
private static final SingletonStarving INSTANCE = new SingletonStarving();
private SingletonStarving() {
}
public static SingletonStarving getInstance() {
return INSTANCE;
}
}
懒汉式单例实现,代码如下:
package javase.singleton;
/**
* 懒汉式单例
*
* @author Realfighter
*
*/
public class SingletonIdler {
private static SingletonIdler INSTANCE = null;
private SingletonIdler() {
}
public synchronized static SingletonIdler getInstance() {
if (INSTANCE == null) {
INSTANCE = new SingletonIdler();
}
return INSTANCE;
}
}
以上实现方式中,懒汉式是比较特殊的,存在多线程并发的问题,所以需要synchronized关键字来确保同步,不过同步对资源性能损耗严 重,getInstance方法耗时严重,为了解决此问题,这里有一个Double-Checked Locking(双重检查加锁)的概念,这是我忽略的一个概念,来减少时间的耗费,示例代码如下:
package javase.singleton;
public class SingletonDoubleChecked {
private static SingletonDoubleChecked INSTANCE = null;
private SingletonDoubleChecked() {
}
public static SingletonDoubleChecked getInstance() {
if (INSTANCE == null) {
//检查实例,如果为空,就进入同步区域
synchronized (SingletonDoubleChecked.class) {
//进入同步区域后,再检查一次,如果仍为空,才创建实例
if (INSTANCE == null) {
INSTANCE = new SingletonDoubleChecked();
}
}
}
return INSTANCE;
}
}
不过即便如此,依然无法保证返回唯一实例对象,这是由于在Java编译器中,类的初始化与INSTANCE变量的赋值顺序不可预料,如果一个线程在没有同 步化的条件下读取INSTANCE的引用,并调用这个对象的方法,可能对象的初始化过程尚未完成,从而造成崩溃,不过这种问题也有解决方法,我们可以通过 类加载器机制,代码如下:
package javase.singleton;
/**
* 类加载器机制解决双重检查加锁问题
* 内部类Instance只被装载一次,
* 保证对象SingletonDoubleCheckedByClassLoader只有一个
*
* @author Realfighter
*
*/
public class SingletonDoubleCheckedByClassLoader {
private static class Instance {
static final SingletonDoubleCheckedByClassLoader instance = new SingletonDoubleCheckedByClassLoader();
}
private SingletonDoubleCheckedByClassLoader() {
}
public static SingletonDoubleCheckedByClassLoader getInstance() {
return Instance.instance;
}
}
我们也可以利用volatile关键字解决,代码如下:
package javase.singleton;
/**
* volatile关键字保证,
* INSTANCE变量被初始化为Singleton实例时,
* 多个线程能正确的处理INSTANCE变量
*
* @author Realfighter
*
*/
public class SingletonDoubleCheckedByVolatile {
private volatile static SingletonDoubleCheckedByVolatile INSTANCE = null;
private SingletonDoubleCheckedByVolatile() {
}
public static SingletonDoubleCheckedByVolatile getInstance() {
if (INSTANCE == null) {
synchronized (SingletonDoubleCheckedByVolatile.class) {
if (INSTANCE == null) {
INSTANCE = new SingletonDoubleCheckedByVolatile();
}
}
}
return INSTANCE;
}
}
JDK1.5,提出了一种新特性:enum枚举,通过enum枚举实现单例非常的简单,而且在枚举里面创建实例也是线程安全的,是强烈推荐使用的,代码如下:
package javase.singleton;
/**
* 枚举实现单例
* @author Realfighter
*
*/
public enum SingletonEnum {
INSTANCE;
}
我们只需要使用SingletonEnum.INSTANCE就可以获取它的唯一实例了,总的来说,单例模式是众多设计模式中比较简单的一个,也是很重要的一种模式,在工作中的某些需求中也有可能会经常用到,还是需要好好学习哈。