【问题标题】:Concurrent access to List within Map并发访问 Map 中的 List
【发布时间】:2012-08-19 06:06:00
【问题描述】:

我已经编写了一个任务侦听器模型,如下代码所示,是否可以对其进行任何优化或错误修复(如果有),尤其是 List 和 Map 访问? 在这个模型中,创建一个 Map 来保存多个 List,其中包含一些用户的监听器,每个 List 的大小大多在 1000 左右。 这里有两点: 1.为了性能,使用HashMap还是ConcurrentHashMap? 2.这是否比复制 List 并在多线程访问时迭代复制的 List 更好?

public class TaskActionManager {

    private static final Map<String, List<TaskActionListener>> listenersMap = new ConcurrentHashMap<String, List<TaskActionListener>>();
    private static final ExecutorService executor = Executors
            .newCachedThreadPool();
    private static ReentrantLock lock = new ReentrantLock();

    public static final void addListener(TaskActionListener listener,
            TaskActionType type) {
        // type is an enum
        String key = type.name();

        List<TaskActionListener> list = listenersMap.get(key);
        lock.lock();
        try {
            // Mostly the list is not null
            if (list == null) {
                list = listenersMap.get(key);
                if (list == null) {
                    list = new ArrayList<TaskActionListener>();
                    listenersMap.put(key, list);
                }
            }
            list.add(listener);
        } finally {
            lock.unlock();
        }
    }

    public static final void removeListener(TaskActionListener listener,
            TaskActionType type) {
        List<TaskActionListener> list = listenersMap.get(type.name());
        if (list == null)
            return;

        lock.lock();
        try {
            list.remove(listener);
        } finally {
            lock.unlock();
        }
    }

    public static final void fireAction(final TaskAction action) {
        executor.execute(new Runnable() {
            @Override
            public void run() {
                List<TaskActionListener> list = listenersMap.get(action
                        .getType().name());
                if (list == null)
                    return;

                // Make a copy
                List<TaskActionListener> copy = null;
                lock.lock();
                try {
                    copy = new ArrayList<TaskActionListener>(list.size());
                    Collections.copy(list, copy);
                } finally {
                    lock.unlock();
                }

                // Iterate the copy
                if (copy != null) {
                    for (TaskActionListener listener : copy) {
                        try {
                            listener.fireAction(action);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        });
    }
}

【问题讨论】:

  • 为什么不直接使用CopyOnWriteArrayList?另外,你为什么在这里使用锁?
  • 据我所知,CopyOnWriteArrayList 通过复制底层数组来避免多线程问题,我不确定这是一个好的选择,因为操作(添加,删除)将在用户登录时调用添加侦听器和注销以删除侦听器。
  • ...这不是你在这里手动做的吗?

标签: java list concurrency map


【解决方案1】:

为了性能,使用 HashMap 还是 ConcurrentHashMap?

显然,ConcurrentHashMap 是 HashMap 的并发版本。所以答案无疑是 ConcurrentHashMap。它使用起来更简单,也更高效,因为你不需要锁定它,因为它是线程安全的。

这是否比复制 List 并在多线程访问时迭代复制的 List 更好?

ConcurrentHashMap 是线程安全的,因此您无需复制即可进行迭代。如果您不介意在迭代列表时(通过另一个线程)更改列表,我不明白您为什么应该在这里,那么这就是要走的路。

查看详情:http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/ConcurrentHashMap.html

注意:

  1. 迭代器设计为一次只能由一个线程使用。
  2. 与 Hashtable 类似,但与 HashMap 不同,此类不允许 null 用作键或值。

【讨论】:

  • 感谢您的回答。对于问题 2,如果我不介意在迭代它时更改列表,即使我只想要一个 List 结构而不是 Map 结构,你的意思是 ConcurrentHashMap 比 List 更合适?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-07-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-07-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多