单例模式
单例模式是指确保一个类在任何情况下都绝对只有一个实例,并提供一个全局访问点。单例模式是创建型模式。
应用场景
Spring框架应用中的ApplicationContext、数据库连接池、JDK中Runtime类等。
public class Runtime {
private static Runtime currentRuntime = new Runtime();
public static Runtime getRuntime() {
return currentRuntime;
}
private Runtime() {
}
}
饿汉式
/**
* 优点:线程绝对安全。执行效率比较高
* 缺点:所有对象加载的时候就要实例化,如果有大批量单例对象创建,占用大量内存,浪费内存资源
*/
public class HungrySingleton {
private static final HungrySingleton singleton = new HungrySingleton();
private HungrySingleton() {
}
public static HungrySingleton getInstance() {
return singleton;
}
}
懒汉式
/**
* 优点:提高内存使用
* 缺点:使用锁,降低执行速度
*/
public class LazySingleton {
private LazySingleton() {
}
private static volatile LazySingleton singleton;
public static LazySingleton getInstance() {
//检查是否要阻塞
if (singleton == null) {
synchronized (LazySingleton.class) {
//判断是创建新的对象
if (singleton == null) {
singleton = new LazySingleton();
//设置lazy指向刚分配的内存地址
//指令重排序的问题
}
}
}
return singleton;
}
}
静态内部类单例
package com.example.springtools.util.singleton;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.ObjectInputStream;
/**
* 1.使用静态内部类创建 兼顾饿汉式单例模式的内存浪费问题和synchronized的性能问题
* 内部类在方法调用之前初始化,避免产生线程安全问题
* 2.构造方法加入对象判断条件 防止使用反射调用构造方法创建多个对象,破坏单例
* 3.使用readResolve方法,防止反序列化的问题
* 通过查看源码得知当对象调用readObject读取对象产生对象时,如果发现对象内部有readResolve,则返回readResolve方法的返回值,
* 如果不存在,则创建新的对象
*/
public class LazyInnerClassSingleton {
private LazyInnerClassSingleton() {
if (LazyHolder.LAZY != null) {
throw new RuntimeException("不允许创建多个实例");
}
}
public static LazyInnerClassSingleton getInstance() {
return LazyHolder.LAZY;
}
private static class LazyHolder {
private static final LazyInnerClassSingleton LAZY = new LazyInnerClassSingleton();
}
// 序列化
// FileInputStream fis = new FileInputStream("xx");
// ObjectInputStream ois = new ObjectInputStream(fis);
// LazyInnerClassSingleton o = (LazyInnerClassSingleton)ois.readObject();
// ois.close();
public LazyInnerClassSingleton readResolve() {
return LazyHolder.LAZY;
}
}
注册式单例模式(登记式单例模式)
枚举式单例模式
/**
* 最优雅的方式
* 通过查看源码发现枚举采用的是饿汉式单例模式的创建方法,当然也存在内存资源消耗的问题,不适合大量单例对象的创建
* 并发安全、不会被反射和反序列化破坏
*/
public enum EnumSingleton {
INSTANCE;
private Object data;
private Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public static EnumSingleton getInstance() {
return INSTANCE;
}
}
容器式单例模式
/**
* 类似懒汉式,线程不安全
*/
public class ContainerSingleton {
private ContainerSingleton() {
}
private static Map<String, Object> ioc = new ConcurrentHashMap<>();
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;
} else {
return ioc.get(className);
}
}
}
}
线程单例实现ThreadLocal
/**
* ThreadLocal不能保证全局唯一,但是可以保证单个线程唯一,天生线程安全
* 每个对象都会放到ThreadLocalMap中,为每个线程提供一个对象,空间换实现实现线程隔离。
*/
public class ThreadLocalSingleton {
private static final ThreadLocal<ThreadLocalSingleton> threadLocalInstance =
new ThreadLocal<ThreadLocalSingleton>() {
@Override
protected ThreadLocalSingleton initialValue() {
return new ThreadLocalSingleton();
}
};
private ThreadLocalSingleton() {
}
public static ThreadLocalSingleton getInstance() {
return threadLocalInstance.get();
}
}
总结:
单例模式可以保证内存里只有一个实例,减少内存的开销,还可以避免对资源的多重占用。