今天回头看之前的单例设计模式的有关记录,在此简单在总结下:
单例模式分为饿汉式和饱汉式(懒汉式),该两个方案在不考虑一些多线程、反射、序列化和反序列化的情况下,
饱汉式易于理解和书写,如下:
public class LazySingleton {
/**
* 初级懒汉式单例设计模式
* 线程不安全的,单线程时不会出问题,但是多线程时会出问题
* 假设两个线程同时运行,第一个线程在判断完if (lazySingleton == null)为true,
* 进入到方法内,但是还未执行创建对象或者还未执行完创建对象的步骤时,
* 此时lazySingleton 还是null,第二个线程刚好走到判断是否为null时,则判断为null,
* 之后线程二也会进入该方法。这样两个线程都进入,则都会正常创建对象, 造成创建两个对象。
*即使最后lazySingleton返回的对象一样(因为lazySingleton被赋值两次,后一次将前一次覆盖)
* 也同样是存在安全隐患。
* */
private static LazySingleton lazySingleton = null;
private LazySingleton(){
}
public static LazySingleton getInstance(){
if (lazySingleton == null){
lazySingleton = new LazySingleton();
}
return lazySingleton;
}
}
饿汉式有两种写法:
(如下是添加了防反射攻击和序列化问题的写法)
饿汉一:
public class HungrySingleton implements Serializable{
//写法简单的单例模式
//类加载时就初始化,没有多线程问题,但是可能会造成资源浪费,
//比如该对象没有被使用
private final static HungrySingleton hungrySingleton = new HungrySingleton();
private HungrySingleton(){
//为构造器添加反射调用判断
if (hungrySingleton != null){
throw new RuntimeException("单例设计模式构造器禁止反射调用");
}
}
public static HungrySingleton getHungrySingleton(){
return hungrySingleton;
}
private Object readResolve(){
return hungrySingleton;
}
}
饿汉二:
public class StaticInnerClassSingleton {
private StaticInnerClassSingleton( ){
//注意该方法不能少,否则会从外面创建一个对象出来。
if (getStaticInnerClassInstance() != null){
throw new RuntimeException("单例设计模式禁止反射调用私有构造器");
}
}
private static class InnerClass{
//该方案重点就在于如下私有类的初始化锁,重点看谁先获取到该初始化锁,
//一旦获取到该初始化锁,会立刻执行初始化,并且该初始化过程对于其他
//线程是不可见的。
private static StaticInnerClassSingleton innerClassSingleton=
new StaticInnerClassSingleton();
}
public static StaticInnerClassSingleton getStaticInnerClassInstance(){
return InnerClass.innerClassSingleton;
}
}
对于如上防止反射攻击的方案,对于饱汉式或者说叫做懒加载的方案是不可行的。
一个简单,而且可以防止多种问题的方案应该属枚举类型做单例的方案了
public enum EnumInstance {
INSTANCE;
private Object data;
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public static EnumInstance getEnumInstance(){
return INSTANCE;
}
}
和单例设计模式相关的一个常见应用为,单例和享元模式结合做成的容器单例:
public class ContainerSingleton {
private ContainerSingleton(){
}
private static Map<String,Object> singletonMap = new HashMap<String, Object>();
public static void putInstance(String key, Object instance) {
if (key.length()>0 && instance != null) {
if (!singletonMap.containsKey(key)) {
singletonMap.put(key, instance);
}
}
}
public static Object getInstance(String key){
return singletonMap.get(key);
}
}
单例设计模式之防止克隆方法:
// 注意这里要实现克隆接口
public class HungrySingleton implements Serializable,Cloneable{
//写法简单的单例模式
//类加载时就初始化,没有多线程问题,但是可能会造成资源浪费,
//比如该对象没有被使用
private final static HungrySingleton hungrySingleton = new HungrySingleton();
//也可以将上述改成如下方式
private HungrySingleton(){
if (hungrySingleton != null){
throw new RuntimeException("单例设计模式构造器禁止反射调用");
}
}
public static HungrySingleton getHungrySingleton(){
return hungrySingleton;
}
private Object readResolve(){
return hungrySingleton;
}
//注意这里的克隆方法
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}