多线程向ConcurrentHashMap存值遇类型转换错误及返回null问题
问题分析与解决方案
核心问题1:线程池任务未完成就返回结果
你调用executor.shutdown()后没有等待所有线程执行完毕,主线程会直接继续执行后续逻辑,此时调用getCrackedPasswords()时,Worker线程还在处理任务,自然返回空集合。而直接调用run()是在主线程同步执行,所有任务完成后才会返回,所以能拿到正确结果。
核心问题2:内部类类型转换错误
你的Worker是非静态内部类,默认持有外部Engine类的实例引用。错误提示里的强制转换问题,是代码中某处误将Engine的this引用强行转为Worker类型导致的(你提供的代码片段未体现,但结合错误信息可推断)。非静态内部类的实例依赖外部类实例,这种隐式引用很容易引发这类转换错误。
修复步骤
1. 等待线程池任务执行完成
修改runProcess()方法,在shutdown()后调用awaitTermination()等待所有线程结束:
public void runProcess() throws InterruptedException { ExecutorService executor = Executors.newFixedThreadPool(NUM_THREADS); for(int i=1; i<=NUM_THREADS; i++) { long workload = numCombs/NUM_THREADS; // 处理最后一个线程的边界,避免numCombs无法被NUM_THREADS整除的情况 long end = (i == NUM_THREADS) ? numCombs : workload * i; Worker worker = new Worker(workload * (i-1), end); executor.execute(worker); } executor.shutdown(); // 等待所有任务完成,超时时间可根据实际场景调整 executor.awaitTermination(1, TimeUnit.HOURS); }
2. 将Worker改为静态内部类
把Worker改成静态内部类,避免持有外部Engine的隐式引用,同时从根源解决类型转换问题:
static class Worker implements Runnable { private final MD4 md4 = new MD4(); private final long iterationStart; private final long iterationEnd; private final ConcurrentHashMap<String, String> crackedPasswords; private final byte[][] hashBytes; // 静态内部类需要显式传入所需的外部类成员 public Worker(long inStart, long inEnd, ConcurrentHashMap<String, String> crackedPasswords, byte[][] hashBytes) { this.iterationStart = inStart; this.iterationEnd = inEnd; this.crackedPasswords = crackedPasswords; this.hashBytes = hashBytes; } @Override public void run() { for(long iteration=iterationStart; iteration<iterationEnd; iteration++) { // 注意:currentPlain需与iteration关联,比如根据iteration生成对应明文,而非固定值 checkHash(String.valueOf(iteration)); } } private void checkHash(String plainText) { for(byte[] hashByte : hashBytes) { if (Arrays.equals(hashByte, md4.runDigest(plainText))){ StringBuilder hex = new StringBuilder(hashByte.length*2); for(byte b: hashByte) hex.append(String.format("%02x", b)); crackedPasswords.putIfAbsent(hex.toString(), plainText); } } } }
创建Worker时传入所需参数:
Worker worker = new Worker(workload * (i-1), end, crackedPasswords, hashBytes);
3. 修复未定义变量与逻辑错误
你提供的代码存在几个未定义变量,需补充完善:
- 调整
Engine构造方法,接收numThreads、numCombs、hashBytes参数:
private final int NUM_THREADS; private final long numCombs; private final byte[][] hashBytes; public Engine(int numThreads, long numCombs, byte[][] hashBytes) { this.NUM_THREADS = numThreads; this.numCombs = numCombs; this.hashBytes = hashBytes; }
currentPlain需与iteration绑定,否则所有线程处理的都是同一个值,导致逻辑错误。
额外优化建议
- 使用
Executors.newFixedThreadPool时,线程数建议不超过CPU核心数的2倍,避免线程过多引发上下文切换开销。 - 可用
CountDownLatch替代awaitTermination,更灵活控制等待逻辑:
public void runProcess() throws InterruptedException { CountDownLatch latch = new CountDownLatch(NUM_THREADS); ExecutorService executor = Executors.newFixedThreadPool(NUM_THREADS); for(int i=1; i<=NUM_THREADS; i++) { long workload = numCombs/NUM_THREADS; long end = (i == NUM_THREADS) ? numCombs : workload * i; Worker worker = new Worker(workload * (i-1), end, crackedPasswords, hashBytes, latch); executor.execute(worker); } executor.shutdown(); latch.await(); // 等待所有线程完成 }
对应的Worker类需添加CountDownLatch参数,并在run方法最后调用latch.countDown():
static class Worker implements Runnable { private final CountDownLatch latch; // 其他成员... public Worker(..., CountDownLatch latch) { // 初始化逻辑... this.latch = latch; } @Override public void run() { try { // 循环处理逻辑 } finally { latch.countDown(); } } }
内容的提问来源于stack exchange,提问作者Oliver Tran
相关产品推荐
相关产品推荐

