实现懒汉式单例模式,我们通常使用以下两种方式实现。
静态内部类
public class Singleton {
private Singleton() {
}
private static class SingletonHoler {
private static Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonHoler.INSTANCE;
}
}
双重检查锁
public class Singleton {
private static volatile Singleton INSTANCE = null;
private Singleton() {
}
public static Singleton getInstance() {
if (Objects.isNull(INSTANCE)) {
synchronized (Singleton.class) {
if (Objects.isNull(INSTANCE)) {
INSTANCE = new Singleton();
}
}
return INSTANCE;
}
}
}
如果需要创建多个单例对象,也就是map中的value需要实现单例模式,有哪些实现方式?
静态内部类不可行
显然静态内部类适用于属性为固定值的对象,对于拥有可变属性的对象并不适合。
redis不可行
因为redis在进行读取的时候需要进行序列化和反序列化,而每次反序列化都是创建一个新的对象。
双重检查锁可行,但是麻烦
双重检查锁有一个关键的步骤就是给单例对象的引用设置volatile,使得该引用的赋值禁止重排序,从而保证引用对象的可见性。因为volatile无法修饰局部变量,所以需要定义成员变量作为中介。
public class AccountRepository {
// 数据库
AccountDb accountDb = new AccountDb();
// 缓存
Map<Integer, Account> cache = new ConcurrentHashMap();
// 因为volatile无法修饰局部变量所以定义了成员变量
volatile Account account;
public Account getById(int id) {
if (!cache.containsKey(id)) {
// 缓存中不存在,那么就创建
initCache(id);
}
return cache.get(id);
}
private synchronized void initCache(int id) {
if (!cache.containsKey(id)) {
account = accountDb.getById(id);
cache.put(id, account);
account = null;
}
}
}
ConcurrentHashMap.computeIfAbsent可行
ConcurrentHashMap.computeIfAbsent内部通过Unsafe类已经实现了变量的可见性处理。
public class AccountRepository {
// 数据库
AccountDb accountDb = new AccountDb();
// 缓存
Map<Integer, Account> cache = new ConcurrentHashMap();
public Account getById(int id) {
if (!cache.containsKey(id)) {
initCache(id);
}
return cache.get(id);
}
private void initCache(int id) {
cache.computeIfAbsent(id, key -> accountDb.getById(key));
}
}
ConcurentHashMap部分代码
public class ConcurrentHashMap<K,V>{
private static final sun.misc.Unsafe U;
static final <K,V> void setTabAt(Node<K,V>[] tab, int i, Node<K,V> v) {
// 通过unsafe类来赋值,实现可见性赋值,禁止重排序
U.putObjectVolatile(tab, ((long)i << ASHIFT) + ABASE, v);
}
}