You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Callable多线程应用中正确使用ConcurrentHashMap?

问题分析与解决:基于Callable的ConcurrentHashMap多线程添加问题

你遇到的问题主要有两个核心错误,导致最终输出的Map为空,且任务执行顺序和打印格式混乱,以下是具体分析和修正方案:

核心错误点

  • 错误1:Callable中误用局部HashMap
    在call()方法里新建了HashMap<String, String> map = new HashMap<>();,这会覆盖构造方法传入的全局ConcurrentHashMap引用,导致所有符合条件的名字都被添加到局部map里,而非共享的ConcurrentHashMap,所以主线程的map始终为空。

  • 错误2:主线程未等待所有线程任务完成
    executor.shutdown()只是关闭线程池的新任务提交入口,不会等待已提交的任务执行完毕。主线程直接打印map时,线程池中的任务可能还在运行,就算修复第一个问题,也可能拿到不完整的结果。

  • 附加问题:打印格式缺失空格
    原代码中打印字符串时没有在名字和后续文本间加空格,导致输出出现Marialength这类拼接错误。


修正后的代码

1. 修正后的Callable实现类

import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;

public class AddElementsToMapCallable implements Callable<ConcurrentHashMap<String, String>> {

    private String name;
    private String className;
    private ConcurrentHashMap<String, String> map;

    public AddElementsToMapCallable(String name, ConcurrentHashMap<String, String> map) {
        this.className = this.getClass().getSimpleName();
        this.name = name;
        this.map = map;
    }

    @Override
    public ConcurrentHashMap<String, String> call() throws Exception {
        System.out.printf("%scall ThreadID = %d Processing User = %s%n",
                className, Thread.currentThread().getId(), name);
        if (name.length() <= 5) {
            System.out.printf("%scall ThreadID = %d Processing User = %s length is smaller than 5. Adding!%n",
                    className, Thread.currentThread().getId(), name);
            // 直接操作共享的ConcurrentHashMap,而非局部map
            map.put(name, String.valueOf(name.length()));
        } else {
            System.out.printf("%scall ThreadID = %d Processing User = %s length is BIGGER than 5%n",
                    className, Thread.currentThread().getId(), name);
        }
        return map;
    }

    public long getThreadId() {
        return Thread.currentThread().getId();
    }

    public Thread getThread() {
        return Thread.currentThread();
    }
}

2. 修正后的主类代码

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class AddElementsToMap {

    public static void main(String[] args) {
        ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>();

        List<String> list = new ArrayList<>();
        list.add("Dimitrios");
        list.add("Maria");
        list.add("Jason");
        list.add("Io");
        list.add("Jessica");

        ExecutorService executor = Executors.newFixedThreadPool(3);
        ArrayList<Future<ConcurrentHashMap<String, String>>> futureList = new ArrayList<>();

        for (String name : list) {
            Future<ConcurrentHashMap<String, String>> future = executor.submit(new AddElementsToMapCallable(name, map));
            futureList.add(future);
        }

        executor.shutdown();
        // 优雅等待所有任务执行完毕
        try {
            executor.awaitTermination(10, java.util.concurrent.TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }

        System.out.println("MAP " + map);
    }
}

修正说明

  1. 移除Callable中的局部HashMap,直接操作构造方法传入的共享ConcurrentHashMap,确保数据能正确写入目标集合。
  2. 主线程使用executor.awaitTermination()等待所有任务完成,避免提前打印空的或不完整的Map。
  3. 使用printf格式化输出,解决原代码中字符串拼接无空格的问题,让日志输出更清晰。

内容的提问来源于stack exchange,提问作者Ioanna Katsanou

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 22:20:32