我有一张队列地图。访问映射和队列必须是线程安全的。不同的线程会在队列中添加和删除数据。
我实现了这个结构
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;
}
}
答案 0 :(得分:5)
由于HashMap的读取没有被任何锁保护,因此代码不正确。因此从地图读取数据的线程不知道任何其他线程已更新它。
它效率也不高;您的结构不会同时支持enqueue
和dequeue
两个线程,并且使用两个同步块是资源消耗。您也可以同步两种方法。
更有效的实现是使用并发数据结构:
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
中的哈希映射不受锁保护。当并发写入使映射处于不一致的内部状态时,您最终可能会尝试读取。