1. 单例模式(singleton pattern)定义
是指确保一个类在任何情况下都绝对只有一个实例,并提供一个全局访问点。属于创建型模式
优点:在内存中只有一个实例,减少了内存开销.
可以避免对资源的多重占用
设置全局访问点,严格控制访问
缺点:没有接口,扩展困难
如果要扩展单例对象,只有修改代码,没有其他途径
2. 单例模式常见写法
饿汉式单例
懒汉式单例
注册式单例
ThreadLocal单例
3. 饿汉式单例
在单例类首次加载时就创建实例
缺点:不使用也创建实例,浪费内存空间
package com.wb.pattern.singleton.hungry;
/**
* 饿汉式单例
* @author wb
*
*/
public class HungrySingleton {
// 加载时创建实例
private static final HungrySingleton hungrySingleton = new HungrySingleton();
// 私有化构造
private HungrySingleton() {}
// 全局访问点
public static HungrySingleton getInstance() {
return hungrySingleton;
}
}
4. 懒汉式单例
被外部调用的时候才创建实例
package com.wb.pattern.singleton.lazy;
/**懒汉式单例
* @author wb
*
*/
public class LazySimpleSingleton {
private static LazySimpleSingleton lazy = null;
// 私有化构造
private LazySimpleSingleton() {}
// 调用的时候才创建实例
// synchronized在jdk1.6之后性能优化了不少,但是不可避免的还是会存在一定的性能问题
public synchronized static LazySimpleSingleton getInstance() {
if (lazy == null ) {
lazy = new LazySimpleSingleton();
}
return lazy;
}
}
双重检查锁单例
package com.wb.pattern.singleton.lazy;
/**
* @author wb
*
*/
public class LazyDoubleCheckSingleton {
private static LazyDoubleCheckSingleton lazy = null;
// 私有化构造
private LazyDoubleCheckSingleton() {}
// 双重检查锁
public static LazyDoubleCheckSingleton getInstance() {
if (lazy == null ) {
synchronized (LazyDoubleCheckSingleton.class) {
if (lazy == null) {
lazy = new LazyDoubleCheckSingleton();
}
}
}
return lazy;
}
}
静态内部类单例
解决线程安全问题
有可能被反射攻击
序列化破坏单例–例子不做了
package com.wb.pattern.singleton.lazy;
/**静态内部类
* @author wb
* 全程没有用到synchronize,所以性能提高
*/
public class LazyInnerClassSingleton {
// 构造方法私有化
private LazyInnerClassSingleton() {}
// LazyHolder里面的逻辑需要外部调用getInstance时才执行,巧妙利用了内部类的特性,避免线程安全问题
public static final LazyInnerClassSingleton getInstance() {
return LazyHolder.LAZY;
}
// 静态内部类
private static class LazyHolder{
private static final LazyInnerClassSingleton LAZY = new LazyInnerClassSingleton();
}
}
反射攻击代码如下,运行结果是false
public class LazyInnerClassSingletonTest {
public static void main(String[] args) {
try {
Class<?> clazz = LazyInnerClassSingleton.class;
Constructor c = clazz.getDeclaredConstructor(null);
c.setAccessible(true);// 强制访问
Object o1 = c.newInstance();
Object o2 = LazyInnerClassSingleton.getInstance();
System.out.println(o1 == o2);
} catch(Exception e ) {
e.printStackTrace();
}
}
}
解决办法
在类的私有化构造的方法中添加判断,如果实例不等于null抛出异常
if (LazyHolder.LAZY != null) {
throw new RuntimeException("不允许构建多个实例");
}
运行结果如下
java.lang.reflect.InvocationTargetException
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at com.wb.pattern.test.LazyInnerClassSingletonTest.main(LazyInnerClassSingletonTest.java:13)
Caused by: java.lang.RuntimeException: 不允许构建多个实例
at com.wb.pattern.singleton.lazy.LazyInnerClassSingleton.<init>(LazyInnerClassSingleton.java:14)
... 5 more
5.注册式单例
将每一个实例都缓存都统一的容器中,使用唯一标识获取实例
用枚举实现单例是最安全的,属于注册式单例
package com.wb.pattern.singleton.register;
/**枚举式单例
* @author wamgbo
*
*/
public enum EnumSingleton {
INSTANCE;
public static EnumSingleton getInstance() {
return INSTANCE;
}
}
容器式单例
package com.wb.pattern.singleton.register;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
1. @author wb
2. */
public class ContainerSingleton {
// 私有化构造
private ContainerSingleton() {}
private static Map<String, Object> ioc = new ConcurrentHashMap<String, Object>();
public static Object getBean(String className) {
synchronized (ioc) {
if (!ioc.containsKey(className)) {
Object obj = null;
try {
obj = Class.forName(className).newInstance();
ioc.put(className, obj);
} catch (Exception e ) {
e.printStackTrace();
}
return obj;
}
return ioc.get(className);
}
}
}
6. ThreadLocal单例
一个线程内是安全的
package com.wb.pattern.singleton.threadlocal;
/**ThreadLocal式单例
* @author wamgbo
*
*/
public class ThreadLocalSingleton {
// 私有化构造
private ThreadLocalSingleton() {}
private static final ThreadLocal<ThreadLocalSingleton> threadLocalInstance =
new ThreadLocal<ThreadLocalSingleton>() {
@Override
protected ThreadLocalSingleton initialValue() {
return new ThreadLocalSingleton();
}
};
//全局访问点
public static ThreadLocalSingleton getInstance() {
return threadLocalInstance.get();
}
}