public static void register(Class<? extends Event> eventClass, Subscriber subscriber) { CopyOnWriteArraySet<Subscriber> set = SUBSCRIBER_MAP.get(eventClass); if (set == null) { set = new CopOnWriteArraySet<Subscriber>(); // 这里有点意思,判断了两次是不是 null,这样能够线程安全吗? CopyOnWriteArraySet<Subscriber> old = SUBSCRIBER_MAP.putIfAbsent(eventClass, set); if (old != null) { set = old; } } set.add(subscriber); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Register subscriber: {} of event: {}.", subscriber, eventClass); } }
两次判断 null,是不是从线程安全的角度考虑的
1 raynor2011 2018-05-27 17:39:48 +08:00 没法保证,old != null 和 set = old 中间一样有可能被另一个线程改写 |
2 MrXiong OP @raynor2011 那为什么这么做呢 |
3 MrXiong OP @raynor2011 我看了代码没有别的地方改写这个 Set |
4 neoblackcap 2018-05-27 17:50:24 +08:00 没法保证线程安全,想安全,老实上锁 |
5 MrXiong OP 补充:map 是 ` private final static ConcurrentHashMap<Class<? extends Event>, CopyOnWriteArraySet<Subscriber>> SUBSCRIBER_MAP = new ConcurrentHashMap<Class<? extends Event>, CopyOnWriteArraySet<Subscriber>>();` |
6 raynor2011 2018-05-27 17:51:26 +08:00 @MrXiong 两个线程同时调用 register 就可能出错 |
7 alamaya 2018-05-27 17:55:49 +08:00 ![]() ConcurrentHashMap 本来就是线程安全,这里 putIfAbsent 就是为了保证只有一个初始化的 set 被装入 map 里 |
8 raynor2011 2018-05-27 18:08:37 +08:00 @alamaya 但是 old != null 的判断,和把对象加入 set, 这一步不是原子的,并不能保证线程安全, 正确的方案应该是提供一个线程安全的 add_new 方案,这样如果之前以及初始化过了,就不会再添加一遍 |
9 MrXiong OP @raynor2011 由于没有 remove 方法所以一旦 put 到 map 中就不会删除,所以下面的 old != null 的判断不需要保证安全性,只需要保证只有一个初始化的 set 放到 map 里就行,因此这个方法是没有问题的 |