如何在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); } }
修正说明
- 移除Callable中的局部HashMap,直接操作构造方法传入的共享
ConcurrentHashMap,确保数据能正确写入目标集合。 - 主线程使用
executor.awaitTermination()等待所有任务完成,避免提前打印空的或不完整的Map。 - 使用
printf格式化输出,解决原代码中字符串拼接无空格的问题,让日志输出更清晰。
内容的提问来源于stack exchange,提问作者Ioanna Katsanou
相关产品推荐
相关产品推荐

