Hashmap是 非线程安全的。因为 Hashap的内部实现并没有加锁,多个线程同时访问和修改时可能会引发数据竞争,导致数据不一致或陷入死循环等问题。
要实现一个线程安全的 HashMap,有多种设计方案,下面是几种常见的实现方法:
使用 Collections.synchronizedMap
Java 提供了一个简单的方法,可以将非线程安全的 Hashnap 包装为线程安全的版本:
Map<String, String> synchronizedMap = Collections.synchronizedMap(new HashMap<>());这种方法通过在 Hashmap的方法上加 synchronized 锁实现线程安全。不过,这种方式对整个 map加锁,会导致较高的锁竞争和性能开销,尤其是在高并发情况下。
不使用锁的设计方案
如果要实现一个线程安全的 Hashmap且不使用锁,可以考虑以下几种方案
CAS(Compare-And-Swap)+分段机制
可以借鉴 concurrentHashmap 的思想,使用分段机制结合 CAS 操作来减少锁的需求。以下是实现思路:
- 分段机制:将 Map分成多个分段(如 16 个分段),每个分段是一个小的 Hashap。通过 key的 hash 值定位到具体的分段,这样可以减少锁的粒度,
- CAS操作:CAS是一种无锁操作,可以避免传统锁带来的性能开销,CAS 操作检查某个变量是否等于期望值,如果是,则将其更新为新值;否则,返回失败,使用CAS 操作可以在无锁的情况下实现线程安全的写入。
- 内部状态控制:对于需要扩容的操作,可以借助 CAS 和重试机制,比如扩容时,只对需要扩容的分段进行扩展,而不是整个 Map,以减少性能影响。
Copy-on-Write 机制
copy-on-write 是一种无锁实现的思路,适合读多写少的场景。实现思路如下:
- 每次写入操作 (如 put 或 remove ) 时,复制当前的 Map,在副本上进行修改,然后将副本替换为当前的 Map。
- 读取时始终访问当前的 Map实例,保证不会受到写入操作的影响。
这种方式的缺点是每次写入都需要复制整个 Map,会带来较大的内存和性能开销,所以只适合读多写少的场景。
