Java 对映射内列表的并发访问

Java 对映射内列表的并发访问,java,list,concurrency,map,Java,List,Concurrency,Map,我已经编写了一个任务侦听器模型,如下代码所示,是否可以对其进行任何优化或bug修复(如果有),尤其是列表和地图访问? 在这个模型中,创建一个映射来保存多个列表,其中包含一些用户侦听器,每个列表的大小通常在1000左右。 这里有两点: 1.为了提高性能,使用HashMap还是ConcurrentHashMap? 2.这是一种比复制列表并在多线程访问时迭代复制的列表更好的方法吗 public class TaskActionManager { private static final Ma

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

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();
                        }
                    }
                }
            }
        });
    }
}
公共类TaskActionManager{
私有静态最终映射listenersMap=new ConcurrentHashMap();
私有静态最终执行器服务执行器=执行器
.newCachedThreadPool();
私有静态ReentrantLock=新的ReentrantLock();
公共静态最终void addListener(TaskActionListener listener,
TaskActionType(类型){
//类型是一个枚举
String key=type.name();
List=listenersMap.get(键);
lock.lock();
试一试{
//大多数情况下,列表不是空的
if(list==null){
list=listenersMap.get(键);
if(list==null){
列表=新的ArrayList();
ListenerMap.put(键、列表);
}
}
添加(侦听器);
}最后{
lock.unlock();
}
}
公共静态最终无效移除侦听器(TaskActionListener侦听器,
TaskActionType(类型){
List=listenersMap.get(type.name());
if(list==null)
返回;
lock.lock();
试一试{
删除(侦听器);
}最后{
lock.unlock();
}
}
公共静态最终无效操作(最终任务操作){
executor.execute(新的Runnable(){
@凌驾
公开募捐{
List=listenersMap.get(操作
.getType().name());
if(list==null)
返回;
//复印
列表副本=空;
lock.lock();
试一试{
copy=newarraylist(list.size());
收藏。副本(列表,副本);
}最后{
lock.unlock();
}
//迭代副本
如果(复制!=null){
for(TaskActionListener侦听器:复制){
试一试{
fireAction(动作);
}捕获(例外e){
e、 printStackTrace();
}
}
}
}
});
}
}
为了提高性能,使用HashMap还是ConcurrentHashMap

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

这是一种比复制列表并在多线程访问时迭代复制的列表更好的方法吗

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();
                        }
                    }
                }
            }
        });
    }
}
ConcurrentHashMap是线程安全的,因此您不需要为了能够迭代而创建副本。如果您不介意在迭代列表时(通过另一个线程)更改列表,我不明白您为什么要在这里更改列表,那么这就是方法

有关更多详细信息,请参阅:

注意:

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

  • 为什么不干脆用一个?另外,为什么在这里使用锁?正如我所知,CopyOnWriteArrayList通过复制底层数组来避免多线程问题,我不确定这是一个好的选择,因为当用户登录添加侦听器并注销以删除侦听器时,将调用操作(添加、删除)。。。。这不是你在这里手工做的吗?谢谢你的回答。对于问题2,您的意思是,如果我不介意在遍历列表时更改列表,那么ConcurrentHashMap比List更合适,即使我只是想要一个列表结构而不是映射结构?