大家都知道HashMap不是线程安全的,但是大家的理解可能都不是十分准确。很显然读写同一个key会导致不一致大家都能理解,但是如果读写一个不变的对象会有问题么?看看下面的代码就明白了。
<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> --> 1 import java.util.HashMap;
2 import java.util.Map;
3 import java.util.Random;
4 import java.util.concurrent.ExecutorService;
5 import java.util.concurrent.Executors;
6 import java.util.concurrent.TimeUnit;
7 import java.util.concurrent.atomic.AtomicInteger;
8
9 public class HashMapTest2 {
10 static void doit() throws Exception{
11 final int count = 200;
12 final AtomicInteger checkNum = new AtomicInteger(0);
13 ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(100);
14 //
15 final Map<Long, String> map = new HashMap<Long, String>();
16 map.put(0L, "www.imxylz.cn");
17 //map.put(1L, "www.imxylz.cn");
18 for (int j = 0; j < count; j++) {
19 newFixedThreadPool.submit(new Runnable() {
20 public void run() {
21 map.put(System.nanoTime()+new Random().nextLong(), "www.imxylz.cn");
22 String obj = map.get(0L);
23 if (obj == null) {
24 checkNum.incrementAndGet();
25 }
26 }
27 });
28 }
29 newFixedThreadPool.awaitTermination(1, TimeUnit.SECONDS);
30 newFixedThreadPool.shutdown();
31
32 System.out.println(checkNum.get());
33 }
34
35 public static void main(String[] args) throws Exception{
36 for(int i=0;i<10;i++) {
37 doit();
38 Thread.sleep(500L);
39 }
40 }
41 }
42
结果一定会输出0么?结果却不一定。比如某一次的结果是:
0
3
0
0
0
0
9
0
9
0
查看了源码,其实出现这个问题是因为HashMap在扩容是导致了重新进行hash计算。
在HashMap中,有下面的源码:
<!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--> 1 public V get(Object key) {
2 if (key == null)
3 return getForNullKey();
4 int hash = hash(key.hashCode());
5 for (Entry<K,V> e = table[indexFor(hash, table.length)];
6 e != null;
7 e = e.next) {
8 Object k;
9 if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
10 return e.value;
11 }
12 return null;
13 }
2 if (key == null)
3 return getForNullKey();
4 int hash = hash(key.hashCode());
5 for (Entry<K,V> e = table[indexFor(hash, table.length)];
6 e != null;
7 e = e.next) {
8 Object k;
9 if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
10 return e.value;
11 }
12 return null;
13 }
在indexOf中就会导致计算有偏移。
<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->1 static int indexFor(int h, int length) {
2 return h & (length-1);
3 }
很显然在Map的容量(table.length,数组的大小)有变化时就会导致此处计算偏移变化。这样每次读的时候就不一定能获取到目标索引了。为了证明此猜想,我们改造下,变成以下的代码。
<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->final Map<String, String> map = new HashMap<String, String>(10000);
执行多次结果总是输出:
0
0
0
0
0
0
0
0
0
0
当然了如果只是读,没有写肯定没有并发的问题了。改换Hashtable或者ConcurrentHashMap肯定也是没有问题了。
相关推荐
TBB 开源库及并发 Hashmap 的使用 TBB 开源库是 Intel 公司开发的开源并行计算库,用于在多核平台上进行并行化程序的开发。该库使用标准 C++ 编写,提供了一个灵活的框架来实现数据并行计算。使用 TBB 可以使用户...
1. 多线程环境下并发修改:在多线程环境下,如果多个线程同时对HashMap进行读写操作,可能会导致数据的不一致性和数据丢失。例如,当一个线程正在执行put操作时,另一个线程可能同时在进行get或remove操作,这可能...
4. **并发问题**:HashMap不是线程安全的,这意味着在多线程环境中,同时对HashMap进行读写操作可能会导致数据不一致。如果需要线程安全的哈希表,可以使用`ConcurrentHashMap`。 5. **null值**:HashMap允许键和值...
无锁HashMap的核心在于利用原子操作来实现数据的读写,确保在并发环境中的安全性。在Go中,`sync/atomic`包提供了原子操作的支持,如`Load`、`Store`、`Add`等,它们可以保证在多线程环境下,对变量的修改不会被中断...
常见的锁包括互斥锁(mutex)、读写锁(rw-lock)以及信号量(semaphore)等。 2. 内存管理:共享内存需要考虑内存分配、释放和内存碎片问题。在多进程环境中,使用`shmctl()`等函数来管理共享内存段的生命周期。 3....
在多线程环境下,Java的`HashMap`类在处理并发操作时容易出现线程安全问题。本文档深入探讨了`HashMap`在多线程环境中可能遇到的安全问题,并提出了一系列可行的解决方案。 #### 二、HashMap线程安全问题分析 在多...
`ConcurrentHashMap`的更新操作(如put、remove等)通常比`HashMap`更复杂,因为它需要确保在并发环境下也能保持数据一致性。 在性能方面,`HashMap`在单线程环境中通常比`ConcurrentHashMap`更快,因为没有额外的...
5. **并发集合**:书中详细讲解了并发环境下如何使用ArrayList、LinkedList、HashSet、HashMap等集合,以及并发安全的ConcurrentHashMap、CopyOnWriteArrayList等并发集合类。 6. **原子变量(Atomic Variables)**...
ConcurrentHashMap采用了分段锁(Segment)的设计,允许不同的段独立进行读写操作,从而提高了并发性。 2. **CopyOnWriteArrayList**:一个线程安全的列表,它的特点是写操作会创建一个新的底层数组,并将旧数组中...
- **ConcurrentHashMap**:线程安全的哈希表,性能优于synchronized HashMap。 - **CopyOnWriteArrayList/CopyOnWriteArraySet**:写时复制策略,读操作无锁,适合读多写少的场景。 - **BlockingQueue**:阻塞...
而在多线程并发环境中,特别是需要高并发读写操作时,`ConcurrentHashMap`是更好的选择。 总的来说,`HashMap`和`ConcurrentHashMap`都是Java中处理键值对的重要工具,它们各自具有不同的特性和使用场景。理解它们...
在“hashmap-thread-test”项目中,通常会创建多个线程并发地读写`HashMap`,然后检查结果以观察是否出现预期的并发问题。这可能包括查看是否出现了数据不一致、死循环或异常抛出等情况。使用Maven的`exec:java`命令...
"dashmap"是一个高效且并发友好的HashMap实现,专为Rust设计。这个库旨在提供线程安全、高性能的解决方案,用于存储和操作键值对。本文将深入探讨dashmap的核心特性、工作原理以及如何在实际项目中利用它。 首先,...
总结来说,C++中的高性能并行HashMap设计是一个涉及并发控制、负载平衡和冲突解决的复杂任务。通过理解并掌握这些关键概念和技术,开发者可以构建出适合大规模并发环境的高效数据结构,从而提升应用程序的整体性能。
ConcurrentHashMap使用分段锁策略,允许并发的读写操作,提高了性能。 此外,HashMap不保证元素的顺序,特别是当进行迭代时,插入顺序可能不会得到保留。如果需要保持插入顺序,可以考虑使用LinkedHashMap,它维护...
5. **并发设计模式**:书中详细阐述了几种常见的并发设计模式,如生产者消费者模式、读写锁模式、双检锁模式(DCL)等,这些都是解决特定并发问题的有效策略。 6. **线程池**:Java的Executor框架提供了线程池管理,...
4. **并发容器**:ArrayList、LinkedList、HashMap、ConcurrentHashMap等容器在并发环境下的性能和安全问题。特别关注ConcurrentHashMap的分段锁策略和无锁数据结构的应用。 5. **线程池原理**:ThreadPoolExecutor...
9. **并发模式**:包括生产者消费者模型、读写锁策略、工作窃取模式等,这些模式可以帮助开发者解决特定的并发问题。 10. **线程通信**:`wait()`, `notify()`, `notifyAll()`是Java中基于对象监视器的线程通信方法...
7. **使用场景**:`ConcurrentHashMap`适用于多线程环境下需要高并发读写操作的情况,如缓存系统、分布式系统中的数据存储等。如果你需要在并发环境中保证数据一致性,同时追求高性能,`ConcurrentHashMap`是一个...