Comments (9)
当前 master 的 SingletonFactory 代码是这样的:
public final class SingletonFactory { private static final Map<String, Object> OBJECT_MAP = new HashMap<>(); private SingletonFactory() { } public static <T> T getInstance(Class<T> c) { String key = c.toString(); Object instance; synchronized (SingletonFactory.class) { instance = OBJECT_MAP.get(key); if (instance == null) { try { instance = c.getDeclaredConstructor().newInstance(); OBJECT_MAP.put(key, instance); } catch (IllegalAccessException | InstantiationException | NoSuchMethodException | InvocationTargetException e) { throw new RuntimeException(e.getMessage(), e); } } } return c.cast(instance); } }我很疑惑为什么所有单例的对象需要共享同一把锁?他们之间也互不冲突。
其次,代码中似乎没有体现出 double check,目前 master 中代码的逻辑中,无论 OBJECT_MAP 中是否已经存在目标单例对象,都需要先与其他线程竞争锁,这一点是不是不太合理?
基于这些存在的问题, 是否改成下面的代码会更好?public class SingletonFactory { private static final Map<String, Object> SINGLETON_MAP = new ConcurrentHashMap<>(); private static final Map<Class<?>, Object> LOCK_MAP = new ConcurrentHashMap<>(); private SingletonFactory() { } /** * Class must have no-arguments constructor. */ public static <T> T getInstance(Class<T> clazz) { if (clazz == null) { throw new IllegalArgumentException(); } String key = clazz.toString(); Object instance = SINGLETON_MAP.get(key); if (instance == null) { synchronized (LOCK_MAP.computeIfAbsent(clazz, k -> new Object())) { instance = SINGLETON_MAP.get(key); if (instance == null) { try { instance = clazz.getDeclaredConstructor().newInstance(); SINGLETON_MAP.put(key, instance); } catch (InstantiationException | InvocationTargetException | NoSuchMethodException | IllegalAccessException e) { throw new RuntimeException(e.getMessage(), e); } } } } return clazz.cast(instance); } }通过 ConcurrentHashMap 也可以
public static Object getInstance(Class<Object> c) { String key = c.toString(); if (OBJECT_MAP.containsKey(key)) { return OBJECT_MAP.get(key); } else { return OBJECT_MAP.computeIfAbsent(key, k -> { try { return c.getDeclaredConstructor().newInstance(); } catch (InstantiationException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { throw new RuntimeException(e.getMessage(), e); } }); } }但是这样似乎会因为 ConcurrentHashMap 在内部 CAS instance 而导致 单例失去了意义?
我举个例子,有线程 A ,线程 B,线程 C, 线程 A 跟线程 B 在同一时刻进入了 computeIfAbsent,并且同时 CAS 他们各自 new 出来的 instance,接着其中有一个线程(假设是 A)就会 CAS 成功(并且在线程 B 替换 instance 引用的时候 结束了 getInstance 方法,返回了线程 A 自己 new 出来的 instance),另外一个就会在第二次设置值的时候,去替换掉这个值。
在这个背景为前提下,假设线程 C 在线程 A CAS 成功的时候,恰好进入了if (OBJECT_MAP.containsKey(key))
并且在线程 B 还没替换 instance 的时候, 线程 C 执行了 get 方法。 这样就会导致, 这个程序中这个单例工厂, 单例这个定义被破坏了,在这个时刻以后,线程 A、C 持有了线程 A new 出来的实例, 线程 B 则持有线程 B 自己 new 出来的实例。说的可能有点绕,如果没理解我的意思的话,我会尝试表达的更清楚一些
另外,这个问题我说的很绕很难理解么....
我觉得,其实你这个问题在方法注释上已经写清楚了,computeIfAbsent
整个方法已经是原子的,同时如果有并发情况会阻碍别的 Function<? super K, ? extends V> mappingFunction
的计算
from guide-rpc-framework.
s
当前master的SingletonFactory代码是这样的:
public final class SingletonFactory { private static final Map<String, Object> OBJECT_MAP = new HashMap<>(); private SingletonFactory() { } public static <T> T getInstance(Class<T> c) { String key = c.toString(); Object instance; synchronized (SingletonFactory.class) { instance = OBJECT_MAP.get(key); if (instance == null) { try { instance = c.getDeclaredConstructor().newInstance(); OBJECT_MAP.put(key, instance); } catch (IllegalAccessException | InstantiationException | NoSuchMethodException | InvocationTargetException e) { throw new RuntimeException(e.getMessage(), e); } } } return c.cast(instance); } }我很疑惑为什么所有单例的对象需要共享同一把锁?他们之间也互不冲突。
其次,代码中似乎没有体现出double check,目前master中代码的逻辑中,无论OBJECT_MAP中是否已经存在目标单例对象,都需要先与其他线程竞争锁,这一点是不是不太合理?
基于这些存在的问题, 是否改成下面的代码会更好?
public class SingletonFactory { private static final Map<String, Object> SINGLETON_MAP = new ConcurrentHashMap<>(); private static final Map<Class<?>, Object> LOCK_MAP = new ConcurrentHashMap<>(); private SingletonFactory() { } /** * Class must have no-arguments constructor. */ public static <T> T getInstance(Class<T> clazz) { if (clazz == null) { throw new IllegalArgumentException(); } String key = clazz.toString(); Object instance = SINGLETON_MAP.get(key); if (instance == null) { synchronized (LOCK_MAP.computeIfAbsent(clazz, k -> new Object())) { instance = SINGLETON_MAP.get(key); if (instance == null) { try { instance = clazz.getDeclaredConstructor().newInstance(); SINGLETON_MAP.put(key, instance); } catch (InstantiationException | InvocationTargetException | NoSuchMethodException | IllegalAccessException e) { throw new RuntimeException(e.getMessage(), e); } } } } return clazz.cast(instance); } }
是的,这种性能要好一点。
from guide-rpc-framework.
s
当前master的SingletonFactory代码是这样的:
public final class SingletonFactory { private static final Map<String, Object> OBJECT_MAP = new HashMap<>(); private SingletonFactory() { } public static <T> T getInstance(Class<T> c) { String key = c.toString(); Object instance; synchronized (SingletonFactory.class) { instance = OBJECT_MAP.get(key); if (instance == null) { try { instance = c.getDeclaredConstructor().newInstance(); OBJECT_MAP.put(key, instance); } catch (IllegalAccessException | InstantiationException | NoSuchMethodException | InvocationTargetException e) { throw new RuntimeException(e.getMessage(), e); } } } return c.cast(instance); } }我很疑惑为什么所有单例的对象需要共享同一把锁?他们之间也互不冲突。
其次,代码中似乎没有体现出double check,目前master中代码的逻辑中,无论OBJECT_MAP中是否已经存在目标单例对象,都需要先与其他线程竞争锁,这一点是不是不太合理?
基于这些存在的问题, 是否改成下面的代码会更好?public class SingletonFactory { private static final Map<String, Object> SINGLETON_MAP = new ConcurrentHashMap<>(); private static final Map<Class<?>, Object> LOCK_MAP = new ConcurrentHashMap<>(); private SingletonFactory() { } /** * Class must have no-arguments constructor. */ public static <T> T getInstance(Class<T> clazz) { if (clazz == null) { throw new IllegalArgumentException(); } String key = clazz.toString(); Object instance = SINGLETON_MAP.get(key); if (instance == null) { synchronized (LOCK_MAP.computeIfAbsent(clazz, k -> new Object())) { instance = SINGLETON_MAP.get(key); if (instance == null) { try { instance = clazz.getDeclaredConstructor().newInstance(); SINGLETON_MAP.put(key, instance); } catch (InstantiationException | InvocationTargetException | NoSuchMethodException | IllegalAccessException e) { throw new RuntimeException(e.getMessage(), e); } } } } return clazz.cast(instance); } }是的,这种性能要好一点。
好的,已PR
from guide-rpc-framework.
当前master的SingletonFactory代码是这样的:
public final class SingletonFactory { private static final Map<String, Object> OBJECT_MAP = new HashMap<>(); private SingletonFactory() { } public static <T> T getInstance(Class<T> c) { String key = c.toString(); Object instance; synchronized (SingletonFactory.class) { instance = OBJECT_MAP.get(key); if (instance == null) { try { instance = c.getDeclaredConstructor().newInstance(); OBJECT_MAP.put(key, instance); } catch (IllegalAccessException | InstantiationException | NoSuchMethodException | InvocationTargetException e) { throw new RuntimeException(e.getMessage(), e); } } } return c.cast(instance); } }我很疑惑为什么所有单例的对象需要共享同一把锁?他们之间也互不冲突。
其次,代码中似乎没有体现出double check,目前master中代码的逻辑中,无论OBJECT_MAP中是否已经存在目标单例对象,都需要先与其他线程竞争锁,这一点是不是不太合理?
基于这些存在的问题, 是否改成下面的代码会更好?
public class SingletonFactory { private static final Map<String, Object> SINGLETON_MAP = new ConcurrentHashMap<>(); private static final Map<Class<?>, Object> LOCK_MAP = new ConcurrentHashMap<>(); private SingletonFactory() { } /** * Class must have no-arguments constructor. */ public static <T> T getInstance(Class<T> clazz) { if (clazz == null) { throw new IllegalArgumentException(); } String key = clazz.toString(); Object instance = SINGLETON_MAP.get(key); if (instance == null) { synchronized (LOCK_MAP.computeIfAbsent(clazz, k -> new Object())) { instance = SINGLETON_MAP.get(key); if (instance == null) { try { instance = clazz.getDeclaredConstructor().newInstance(); SINGLETON_MAP.put(key, instance); } catch (InstantiationException | InvocationTargetException | NoSuchMethodException | IllegalAccessException e) { throw new RuntimeException(e.getMessage(), e); } } } } return clazz.cast(instance); } }
通过 ConcurrentHashMap 也可以
public static Object getInstance(Class<Object> c) {
String key = c.toString();
if (OBJECT_MAP.containsKey(key)) {
return OBJECT_MAP.get(key);
} else {
return OBJECT_MAP.computeIfAbsent(key, k -> {
try {
return c.getDeclaredConstructor().newInstance();
} catch (InstantiationException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e.getMessage(), e);
}
});
}
}
from guide-rpc-framework.
当前master的SingletonFactory代码是这样的:
public final class SingletonFactory { private static final Map<String, Object> OBJECT_MAP = new HashMap<>(); private SingletonFactory() { } public static <T> T getInstance(Class<T> c) { String key = c.toString(); Object instance; synchronized (SingletonFactory.class) { instance = OBJECT_MAP.get(key); if (instance == null) { try { instance = c.getDeclaredConstructor().newInstance(); OBJECT_MAP.put(key, instance); } catch (IllegalAccessException | InstantiationException | NoSuchMethodException | InvocationTargetException e) { throw new RuntimeException(e.getMessage(), e); } } } return c.cast(instance); } }我很疑惑为什么所有单例的对象需要共享同一把锁?他们之间也互不冲突。
其次,代码中似乎没有体现出double check,目前master中代码的逻辑中,无论OBJECT_MAP中是否已经存在目标单例对象,都需要先与其他线程竞争锁,这一点是不是不太合理?
基于这些存在的问题, 是否改成下面的代码会更好?public class SingletonFactory { private static final Map<String, Object> SINGLETON_MAP = new ConcurrentHashMap<>(); private static final Map<Class<?>, Object> LOCK_MAP = new ConcurrentHashMap<>(); private SingletonFactory() { } /** * Class must have no-arguments constructor. */ public static <T> T getInstance(Class<T> clazz) { if (clazz == null) { throw new IllegalArgumentException(); } String key = clazz.toString(); Object instance = SINGLETON_MAP.get(key); if (instance == null) { synchronized (LOCK_MAP.computeIfAbsent(clazz, k -> new Object())) { instance = SINGLETON_MAP.get(key); if (instance == null) { try { instance = clazz.getDeclaredConstructor().newInstance(); SINGLETON_MAP.put(key, instance); } catch (InstantiationException | InvocationTargetException | NoSuchMethodException | IllegalAccessException e) { throw new RuntimeException(e.getMessage(), e); } } } } return clazz.cast(instance); } }通过 ConcurrentHashMap 也可以
public static Object getInstance(Class<Object> c) { String key = c.toString(); if (OBJECT_MAP.containsKey(key)) { return OBJECT_MAP.get(key); } else { return OBJECT_MAP.computeIfAbsent(key, k -> { try { return c.getDeclaredConstructor().newInstance(); } catch (InstantiationException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { throw new RuntimeException(e.getMessage(), e); } }); } }
但是这样似乎会因为ConcurrentHashMap 在内部CAS instance而导致 单例失去了意义?
我举个例子,有线程A ,线程B,线程C, 线程A跟线程B在同一时刻进入了computeIfAbsent,并且同时CAS 他们各自new出来的instance,接着其中有一个线程(假设是A)就会CAS成功(并且在线程B替换instance引用的时候 结束了getInstance方法,返回了线程A自己new 出来的instance),另外一个就会在第二次设置值的时候,去替换掉这个值。
在这个背景为前提下,假设线程C在线程A CAS成功的时候,恰好进入了if (OBJECT_MAP.containsKey(key))
并且在线程B还没替换instance的时候, 线程C执行了get方法。 这样就会导致, 这个程序中这个单例工厂, 单例这个定义被破坏了,在这个时刻以后,线程A、C持有了线程A new 出来的实例, 线程B则持有线程B 自己new 出来的实例。
from guide-rpc-framework.
当前master的SingletonFactory代码是这样的:
public final class SingletonFactory { private static final Map<String, Object> OBJECT_MAP = new HashMap<>(); private SingletonFactory() { } public static <T> T getInstance(Class<T> c) { String key = c.toString(); Object instance; synchronized (SingletonFactory.class) { instance = OBJECT_MAP.get(key); if (instance == null) { try { instance = c.getDeclaredConstructor().newInstance(); OBJECT_MAP.put(key, instance); } catch (IllegalAccessException | InstantiationException | NoSuchMethodException | InvocationTargetException e) { throw new RuntimeException(e.getMessage(), e); } } } return c.cast(instance); } }我很疑惑为什么所有单例的对象需要共享同一把锁?他们之间也互不冲突。
其次,代码中似乎没有体现出double check,目前master中代码的逻辑中,无论OBJECT_MAP中是否已经存在目标单例对象,都需要先与其他线程竞争锁,这一点是不是不太合理?
基于这些存在的问题, 是否改成下面的代码会更好?public class SingletonFactory { private static final Map<String, Object> SINGLETON_MAP = new ConcurrentHashMap<>(); private static final Map<Class<?>, Object> LOCK_MAP = new ConcurrentHashMap<>(); private SingletonFactory() { } /** * Class must have no-arguments constructor. */ public static <T> T getInstance(Class<T> clazz) { if (clazz == null) { throw new IllegalArgumentException(); } String key = clazz.toString(); Object instance = SINGLETON_MAP.get(key); if (instance == null) { synchronized (LOCK_MAP.computeIfAbsent(clazz, k -> new Object())) { instance = SINGLETON_MAP.get(key); if (instance == null) { try { instance = clazz.getDeclaredConstructor().newInstance(); SINGLETON_MAP.put(key, instance); } catch (InstantiationException | InvocationTargetException | NoSuchMethodException | IllegalAccessException e) { throw new RuntimeException(e.getMessage(), e); } } } } return clazz.cast(instance); } }通过 ConcurrentHashMap 也可以
public static Object getInstance(Class<Object> c) { String key = c.toString(); if (OBJECT_MAP.containsKey(key)) { return OBJECT_MAP.get(key); } else { return OBJECT_MAP.computeIfAbsent(key, k -> { try { return c.getDeclaredConstructor().newInstance(); } catch (InstantiationException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { throw new RuntimeException(e.getMessage(), e); } }); } }但是这样似乎会因为ConcurrentHashMap 在内部CAS instance而导致 单例失去了意义?
我举个例子,有线程A ,线程B,线程C, 线程A跟线程B在同一时刻进入了computeIfAbsent,并且同时CAS 他们各自new出来的instance,接着其中有一个线程(假设是A)就会CAS成功(并且在线程B替换instance引用的时候 结束了getInstance方法,返回了线程A自己new 出来的instance),另外一个就会在第二次设置值的时候,去替换掉这个值。
在这个背景为前提下,假设线程C在线程A CAS成功的时候,恰好进入了
if (OBJECT_MAP.containsKey(key))
并且在线程B还没替换instance的时候, 线程C执行了get方法。 这样就会导致, 这个程序中这个单例工厂, 单例这个定义被破坏了,在这个时刻以后,线程A、C持有了线程A new 出来的实例, 线程B则持有线程B 自己new 出来的实例。
说的可能有点绕,如果没理解我的意思的话,我会尝试表达的更清楚一些
from guide-rpc-framework.
emmm,其实我有个疑问,为什么要用单例工厂?
如果要用单例模式,为什么不用单元素枚举类,或者私有构造函数然后提供常量呢
from guide-rpc-framework.
emmm,其实我有个疑问,为什么要用单例工厂?
如果要用单例模式,为什么不用单元素枚举类,或者私有构造函数然后提供常量呢
但这项目里面这么多地方依赖单例工厂,如果全改成用单元素枚举,得整多少个枚举类啊。
私有构造函数提供常量的话,我个人觉得还不如一个单例工厂方便管理?
from guide-rpc-framework.
当前master的SingletonFactory代码是这样的:
public final class SingletonFactory { private static final Map<String, Object> OBJECT_MAP = new HashMap<>(); private SingletonFactory() { } public static <T> T getInstance(Class<T> c) { String key = c.toString(); Object instance; synchronized (SingletonFactory.class) { instance = OBJECT_MAP.get(key); if (instance == null) { try { instance = c.getDeclaredConstructor().newInstance(); OBJECT_MAP.put(key, instance); } catch (IllegalAccessException | InstantiationException | NoSuchMethodException | InvocationTargetException e) { throw new RuntimeException(e.getMessage(), e); } } } return c.cast(instance); } }我很疑惑为什么所有单例的对象需要共享同一把锁?他们之间也互不冲突。
其次,代码中似乎没有体现出double check,目前master中代码的逻辑中,无论OBJECT_MAP中是否已经存在目标单例对象,都需要先与其他线程竞争锁,这一点是不是不太合理?
基于这些存在的问题, 是否改成下面的代码会更好?public class SingletonFactory { private static final Map<String, Object> SINGLETON_MAP = new ConcurrentHashMap<>(); private static final Map<Class<?>, Object> LOCK_MAP = new ConcurrentHashMap<>(); private SingletonFactory() { } /** * Class must have no-arguments constructor. */ public static <T> T getInstance(Class<T> clazz) { if (clazz == null) { throw new IllegalArgumentException(); } String key = clazz.toString(); Object instance = SINGLETON_MAP.get(key); if (instance == null) { synchronized (LOCK_MAP.computeIfAbsent(clazz, k -> new Object())) { instance = SINGLETON_MAP.get(key); if (instance == null) { try { instance = clazz.getDeclaredConstructor().newInstance(); SINGLETON_MAP.put(key, instance); } catch (InstantiationException | InvocationTargetException | NoSuchMethodException | IllegalAccessException e) { throw new RuntimeException(e.getMessage(), e); } } } } return clazz.cast(instance); } }通过 ConcurrentHashMap 也可以
public static Object getInstance(Class<Object> c) { String key = c.toString(); if (OBJECT_MAP.containsKey(key)) { return OBJECT_MAP.get(key); } else { return OBJECT_MAP.computeIfAbsent(key, k -> { try { return c.getDeclaredConstructor().newInstance(); } catch (InstantiationException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { throw new RuntimeException(e.getMessage(), e); } }); } }但是这样似乎会因为ConcurrentHashMap 在内部CAS instance而导致 单例失去了意义?
我举个例子,有线程A ,线程B,线程C, 线程A跟线程B在同一时刻进入了computeIfAbsent,并且同时CAS 他们各自new出来的instance,接着其中有一个线程(假设是A)就会CAS成功(并且在线程B替换instance引用的时候 结束了getInstance方法,返回了线程A自己new 出来的instance),另外一个就会在第二次设置值的时候,去替换掉这个值。
在这个背景为前提下,假设线程C在线程A CAS成功的时候,恰好进入了if (OBJECT_MAP.containsKey(key))
并且在线程B还没替换instance的时候, 线程C执行了get方法。 这样就会导致, 这个程序中这个单例工厂, 单例这个定义被破坏了,在这个时刻以后,线程A、C持有了线程A new 出来的实例, 线程B则持有线程B 自己new 出来的实例。说的可能有点绕,如果没理解我的意思的话,我会尝试表达的更清楚一些
另外,这个问题我说的很绕很难理解么....
from guide-rpc-framework.
Related Issues (20)
- github.javaguide.provider.impl.ZkServiceProviderImpl类中为什么有serviceMap,还需要registeredService这个set去保存接口名? HOT 1
- 无法远程调用无参方法 HOT 2
- 请问如果远程接口有多个实现类怎么办 HOT 1
- 请问自定义RPC协议的设计思路是如何? HOT 1
- 请问服务宕机了,zk能感知到吗 HOT 4
- SPI实现的一点小疑问 HOT 2
- RPC服务调用方法参数传递为Java类对象 HOT 3
- 发现个小问题 HOT 1
- CollectionUtils & StringUtils HOT 1
- 一致性hash中,相同入参无法路由到同一个服务 HOT 3
- 关于服务提供者下线后重新上线,不通过重启客户端无法被服务提供者感知 HOT 1
- 有个问题有人可以解答一下吗? HOT 1
- ExtensionLoader类获取实现类的时候,为什么要加双重锁 HOT 1
- 并发情况下,应该会创建多条连接吧?
- InetAddress.getLocalHost().getHostAddress(); HOT 1
- zookeeper 的 watch 机制导致的问题 HOT 1
- zookeeper版本和curator版本不匹配导致的问题 HOT 1
- 获取rpcServiceName的时候直接用接口名、version、group相加是不是不太好 HOT 1
- 序列化方式Kryo写错了
- RpcRequestHandler优化 HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from guide-rpc-framework.