首页 > 解决方案 > 并发哈希图删除复杂值

问题描述

我有哈希图:

private final ConcurrentHashMap<String, List<Client>> clients;

和类:

public static class Client {
    private String name; // it is also the key of the map
    private String url;
}

从多个线程中,我调用线程安全方法“ removeElement ”,它必须从列表中删除一个值。

@Override
public CompletableFuture<Void> removeClient(Client client) {
    return CompletableFuture.runAsync(() ->
            clients.entrySet().removeIf(v ->
                    v.getValue().removeIf(
                            it -> client.url.equals(it.url))
            )
    );
}

但是,当然,它不起作用。当我得到 Method throw 'java.lang.UnsupportedOperationException' 异常时,我解决了这样的问题:

@Override
public CompletableFuture<Void> removeClient(Client client) {
    return CompletableFuture.runAsync(() -> {
                List<Client> currentClients = new ArrayList<>(clients.get(client.getName()));
                currentClients.remove(client);
                if (currentClients.isEmpty()) {
                    clients.remove(client.getName());
                } else {
                    clients.put(client.getName(), currentClients);
                }
            }
    );
}

但它不是线程安全的。我怎样才能在这里实现它?也许有更优雅的方法来解决它?

标签: javamultithreadingdictionarythread-safety

解决方案


我认为您可以ConcurrentHashMap::computeIfPresent在这种情况下使用,假设List没有为相同的键放置相同的实例:

CompletableFuture.runAsync(() -> {
    clients.computeIfPresent(client.getName(), (name, clients1) -> {
        List<Client> currentClients = new ArrayList<>(clients1);
        currentClients.remove(client);
        return currentClients.isEmpty() ? null : currentClients;
    });
});

由于computeIfPresent是原子执行的,并且我们在 remappingFunction 中使用列表的副本 - 它应该可以工作。

正如我们在文档中看到的:

如果指定键的值存在,则尝试在给定键及其当前映射值的情况下计算新映射。整个方法调用以原子方式执行。在计算过程中,其他线程对该映射的某些尝试更新操作可能会被阻塞,因此计算应该简短而简单,并且不得尝试更新该映射的任何其他映射。


推荐阅读