单例模式
问题
多线程操作同一对象保证对象的一致性
解决思路
只有一次实例化过程,产生一个实例化对象,并提供返回该对象的方法。
单例模式的分类
1 饿汉式
在加载类的时候就产生实例对象
public class HungerySingleton {
/**
* 在加载说就产生了实例对象
*/
private static HungerySingleton instance = new HungerySingleton();
private HungerySingleton(){
}
/**
* @return 返回实例对象
*/
public static HungerySingleton getInstance(){
return instance;
}
}
线程安全性:在加载时就已经实例化,所以只有一次实例化,线程是安全的。
不是懒加载
性能不好
2 懒汉式
在加载类时不声明实例对象,当使用该对象时才会实例化对象,不过对象也只能实例化一次。
public class HoonSinglenton {
/**
* 在加载时只声明变量不产生了实例对象
*/
private static HoonSinglenton instance = null;
private HoonSinglenton(){
}
/**
* @return 返回实例对象
*/
public static HoonSinglenton getInstance(){
if (instance == null){
instance = new HoonSinglenton();
}
return instance;
}
}
**线程安全性:不安全,不能保证实例对象的唯一性。**在多线程访问时,如果有两个线程同时进入if判断那么这两个线程获取的对象不是同一对象,不符合单例模式的定义。
是懒加载
性能好
3 懒汉式+同步方法
public class HoonSynSinglenton {
private static HoonSynSinglenton instance = null;
private HoonSynSinglenton(){
}
public synchronized static HoonSynSinglenton getInstance(){
if (instance == null){
instance = new HoonSynSinglenton();
}
return instance;
}
}
线程安全性:安全
是懒加载
性能不好:synchronized修饰的方法在多线程访问时会退化成串行执行。
4 DCL(Double-Check-Locking)
public class DCL {
private static DCL instance = null;
private DCL(){
}
public synchronized static DCL getInstance(){
if (instance == null){
synchronized(DCL.class){
if (instance == null){
instance = new DCL();
}
}
}
return instance;
}
}
性能比较好
懒加载
保证线程安全性
缺点:会因为指令重排序,引起空指针异常
5 volatile + DCL
private volatile static DCL instance = null;
DCL解决方案
6 Holder
使用比较广泛的一种单例模式
在声明类时,成员变量中不声明实例变量,而放到内部静态类中,在静态类中实例化,避免懒汉式中加锁的问题。
public class HolderSinglenton {
private HolderSinglenton(){}
private static class Holder{
private static HolderSinglenton instance = new HolderSinglenton();
}
public static HolderSinglenton getInstance(){
return Holder.instance;
}
}
7 枚举
Effective Java推荐该方法
public class EnumSingletonDemo {
private EnumSingletonDemo(){}
private enum EnumHolder{
/**
* 单例对象
*/
INSTANCE;
private EnumSingletonDemo instance;
EnumHolder(){
this.instance = new EnumSingletonDemo();
}
private EnumSingletonDemo getInstance(){
return instance;
}
}
public static EnumSingletonDemo getInstance(){
return EnumHolder.INSTANCE.getInstance();
}
}
代码中的的枚举类型是一个内部类,内部类在Java中使用是懒加载只有使用时才会加载。加载EnumSingletonDemo类不会引起内部类的加载。