Java Hashtable同步异常:为何无法稳定输出预期值1000?
问题原因解析
Hashtable确实是线程安全的,它的get()和put()方法各自都是同步的,但你代码里的map.put("key", map.get("key")+1)是多个独立同步方法的组合操作,整体不具备原子性。
这个复合操作可以拆成三个独立步骤:
- 调用
map.get("key")获取当前值 - 对获取到的值执行
+1运算 - 调用
map.put()将新值写回
虽然每个步骤单独是线程安全的,但三个步骤之间没有锁的保护,会出现线程交错执行的情况:
- 线程A执行完步骤1,拿到值为
n - 此时线程B抢占CPU,也执行步骤1,同样拿到值
n - 线程A重新获得CPU,完成步骤2和3,把
n+1写入Map - 线程B接着完成步骤2和3,也把
n+1写入Map
这就导致两次操作只让值增加了1,而不是预期的2,最终结果必然小于1000。
修复方案
可以通过以下几种方式保证操作的原子性:
1. 用synchronized块包裹整个复合操作
把get和put的组合操作放到同一个锁块里,确保整个流程是原子的:
Runnable task = () -> { for (int i = 0; i < 10; i++) { synchronized (map) { map.put("key", map.get("key") + 1); } } };
2. 使用AtomicInteger作为Map的值
利用原子类的自增方法来保证操作的原子性,避免手动组合操作:
Map<String, AtomicInteger> map = new Hashtable<>(); map.put("key", new AtomicInteger(0)); Runnable task = () -> { for (int i = 0; i < 10; i++) { map.get("key").incrementAndGet(); } };
3. 使用ConcurrentHashMap的原子复合方法
ConcurrentHashMap提供了compute这类原子性的复合操作方法,无需手动加锁:
Map<String, Integer> map = new ConcurrentHashMap<>(); map.put("key", 0); Runnable task = () -> { for (int i = 0; i < 10; i++) { map.compute("key", (k, v) -> v + 1); } };
内容的提问来源于stack exchange,提问作者sandeep
相关产品推荐
相关产品推荐

