这个结构线程安全吗?

时间:2014-12-01 17:31:28

标签: java multithreading

我有一张队列地图。访问映射和队列必须是线程安全的。不同的线程会在队列中添加和删除数据。

我实现了这个结构

public class Structure {

private final Map<String, LinkedList<String>> queues = new HashMap<>();

public void enqueue(String id, String data) {
    LinkedList<String> queue;
    synchronized (queues) {
        queue = queues.get(id);
        if (queue == null) {
            queue = new LinkedList<>();
            queues.put(id, queue);
        }
    }
    synchronized (queue) {
        queue.add(data);
    }
}

public List<String> dequeue(String id) {
    LinkedList<String> queue;
    synchronized (queues) {
        queue = queues.get(id);
    }
    if (queue != null) {
        synchronized (queue) {
            List<String> q = new LinkedList<>(queue);
            queue.clear();
            return q;
        }
    }
    return null;
}

}

2 个答案:

答案 0 :(得分:5)

由于HashMap的读取没有被任何锁保护,因此代码不正确。因此从地图读取数据的线程不知道任何其他线程已更新它。

它效率也不高;您的结构不会同时支持enqueuedequeue两个线程,并且使用两个同步块是资源消耗。您也可以同步两种方法。

更有效的实现是使用并发数据结构:

public class Structure<K, V> {
    private final Queue<V> EMPTY_QUEUE = new ConcurrentLinkedQueue<>();
    private final Map<K, Queue<V>> queues = new ConcurrentHashMap<>();

    public void enqueue(K id, V data) {
        Queue<V> queue = queues.computeIfAbsent(id, i -> new ConcurrentLinkedQueue<>());
        queue.add(data);
    }

    public List<V> dequeue(K id) {
        Queue<V> queue = queues.getOrDefault(id, EMPTY_QUEUE);
        List<V> resp = new LinkedList<>(queue);
        queue.clear();
        return resp;
    }
}

答案 1 :(得分:2)

非线程安全:读取dequeue中的哈希映射不受锁保护。当并发写入使映射处于不一致的内部状态时,您最终可能会尝试读取。