1. 单例模式(Singleton)
- 一个类只有一个实例, 并提供一个访问该实例的全局节点
- 应用
- ServletContext
- ApplicationContext
2. 实战
public static void main(String[] args) throws Exception {
for (int i = 0; i < 10; i++) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Singleton.getInstance().hashCode());
}
}).start();
}
}
2.1 饿汉式
- 类加载时创建,天生线程安全,以空间换时间
- 优点:线程安全
- 缺点:生命周期太长,浪费空间(不使用也会创建)
public class Singleton {
// final 常量, 私有构造方法
private static final Singleton instance = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return instance;
}
}
2.2 懒汉式
- 使用时创建,线程不安全,需加同步
- 优点:生命周期短,节省空间
- 缺点:有线程安全问题(用同步)
public class Singleton {
private static Singleton instance = null;
private Singleton() {}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
改进:双重校验锁
public class Singleton {
private static Singleton instance = null;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
2.3 静态内部类写法(也是懒汉式)
public class Singleton {
private static Singleton instance = null;
private static class Holder {
static Singleton singleton = new Singleton();
}
public static Singleton getInstance() {
return Holder.singleton;
}
}
优点:外部类加载时并不需要立即加载内部类,内部类不被加载则不去初始化instance,不占内存
只有当 getInstance() 第一次被调用时,才会去初始化INSTANCE,第一次调用getInstance()方法会导致虚拟机加载SingleTonHoler类,这种方法不仅能确保线程安全,也能保证单例的唯一性,同时也延迟了单例的实例化。
2.4 容器式单例
容器式单例写法适用于需要大量创建单例对象的场景,便于管理,但它是非线程安全的
public class ContainerSingleton {
private static Map<String, Object> beanMap = new ConcurrentHashMap<>();
private ContainerSingleton() {
}
public static Object getBean(String className) {
synchronized (beanMap) {
if (beanMap.containsKey(className)) {
return beanMap.get(className);
} else {
Object obj = null;
try {
obj = Class.forName(className).newInstance();
beanMap.put(className, obj);
} catch (Exception e) {
e.printStackTrace();
}
return obj;
}
}
}
}
扩展:Spring中的容器式单例
Spring中的 Bean 默认是单例模式的,框架并没有对bean进行多线程的封装处理,Bean 是全局共享的,所以不是线程安全的
若需要线程安全的 Bean, 改变 Bean 的作用域 把 "singleton" 改为 "protopyte"
网友评论