ConcurrentHashMap同时读取和更新

在本文中,我们将讨论如何使用ConcurrentHashMap通过2个不同的线程同时实现读取(迭代)和修改(删除/添加)操作,而这是简单的HashMap无法实现的

 

1. HashMap:

如果2个不同的线程同时对同一个HashMap对象执行操作,则编译器将引发ConcurrentModificationException

我们将演示一个使用HashMap的简单示例,该示例执行

  • 1个第一螺纹迭代读取的条目一个接一个
  • 2第二螺纹去除键-值对; 而其他线程正在迭代HashMap对象

IterateAndModifyHashMapSimultaneously.java

1个
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18岁
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
package in.bench.resources.concurrent.collection;
 
import java.util.HashMap;
import java.util.Map;
 
// extending Thread class
public class IterateAndModifyHashMap extends Thread {
 
    // creating HashMap object of type <Integer, String>
    static HashMap<Integer, String> hm =
            new HashMap<Integer, String>();
 
    @Override
    public void run() {
 
        try {
            // sleeping thread for 1000 ms
            Thread.sleep(1000);
 
            // removing entry with key=1
            String value = hm.remove(1);
            System.out.println("Entry with {key=1" +
                    " & value=" + value + "} is removed");
        }
        catch(InterruptedException iex) {
            iex.printStackTrace();
        }
        System.out.println("Removal is done... !!");
    }
 
    /**
     * main() method
     * @param args
     * @throws InterruptedException
     */
    public static void main(String[] args) throws InterruptedException {
 
        // adding key-value pairs to HashMap object
        hm.put(1, "google.com");
        hm.put(2, "youtube.com");
        hm.put(3, "facebook.com");
 
        // creating another thread
        Thread newThread = new Thread(new IterateAndModifyHashMap());
        newThread.start();
 
        // iterating HM object using enhanced for-loop
        for(Map.Entry<Integer, String> me : hm.entrySet()) {
 
            System.out.println("{Key=" + me.getKey()
                    + "\t" + "Value=" + me.getValue() + "}");
 
            // sleeping thread for 1500 ms, after every turn
            Thread.sleep(1500);
        }
        System.out.println("Iterating completed... !!");
    }
}

输出:

1个
2
3
4
5
6
7
8
9
{Key=1  Value=google.com}
Entry with {key=1 & value=google.com} is removed
Removal is done... !!
Exception in thread "main" java.util.ConcurrentModificationException
    at java.util.HashMap$HashIterator.nextEntry(HashMap.java:895)
    at java.util.HashMap$EntryIterator.next(HashMap.java:935)
    at java.util.HashMap$EntryIterator.next(HashMap.java:933)
    at in.bench.resources.concurrent.collection
        .IterateAndModifyHashMap.main(IterateAndModifyHashMap.java:48)

说明:

  • 主线程迭代HashMap对象,子线程使用key = 1删除HashMap条目
  • 从输出中可以清楚地看到,当一个线程在HashMap对象上进行迭代,并且是否有其他线程执行修改操作时(即,在同一HashMap对象上,另一个线程正在删除条目)
  • 然后编译器将抛出ConcurrentModificationException
  • 注意:介绍学习示例的sleep(ms)
  • 因为没有睡眠,两个线程都将独立执行(在nano / pico秒内完成其执行),并且不会出现任何编译时错误
  • 由于我们试图用少量的数据来理解(执行在十亿分之一秒内完成)
  • 但是,由于有大量数据,不需要引入睡眠概念
  • 随着每个线程执行时间的增加,肯定会引发ConcurrentModificationException

 

问)如何克服上述HashMap异常?

  • 随着ConcurrentHashMap中,我们可以克服这个问题
  • 因为它适用于不同的锁定策略 或不同的并发级别

 

2. ConcurrentHashMap:

2个不同的线程同时对同一个ConcurrentHashMap对象执行操作时,编译器将不会引发任何运行时异常

这是使用ConcurrentHashMap而不是HashMap优点

在演示示例中,

 
  • 1日线程迭代通过的ConcurrentHashMap的所有键值对
  • 虽然其他线程可以安全地删除key = 1的键/值对
  • 与HashMap不同,编译器不会引发任何ConcurrentModificationException
  • 这是因为ConcurrentHashMap在不同的并发级别或不同的锁定策略下工作

IterateAndModifyConcurrentHashMapSimultaneously.java

1个
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18岁
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
package in.bench.resources.concurrent.collection;
 
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
 
// implementing Runnable interface
public class IterateAndModifyConcurrentHashMap implements Runnable {
 
    // creating ConcurrentHashMap object of type <Integer, String>
    static ConcurrentHashMap<Integer, String> chm =
            new ConcurrentHashMap<Integer, String>();
 
    @Override
    public void run() {
 
        try {
            // sleeping thread for 1000 ms
            Thread.sleep(1000);
 
            // removing entry with key=1
            String value = chm.remove(1);
            System.out.println("Entry with {key=1"
                    + " & value=" + value + "} is removed");
        }
        catch(InterruptedException iex) {
            iex.printStackTrace();
        }
        System.out.println("Removal is done... !!");
    }
 
    /**
     * main() method
     * @param args
     * @throws InterruptedException
     */
    public static void main(String[] args) throws InterruptedException {
 
        // adding key-value pairs to ConcurrentHashMap object
        chm.put(1, "google.com");
        chm.put(2, "youtube.com");
        chm.put(3, "facebook.com");
 
        // creating another thread
        Thread newThread = new Thread(
                new IterateAndModifyConcurrentHashMap());
        newThread.start();
 
        // iterating CHM object using enhanced for-loop
        for(Map.Entry<Integer, String> me : chm.entrySet()) {
 
            System.out.println("{Key=" + me.getKey()
                    + "\t" + "Value=" + me.getValue() + "}");
 
            // sleeping thread for 2000 ms, after every turn
            Thread.sleep(2000);
        }
        System.out.println("Iterating completed... !!");
    }
}

输出:

1个
2
3
4
5
{Key=3  Value=facebook.com}
Entry with {key=1 & value=google.com} is removed
Removal is done... !!
{Key=2  Value=youtube.com}
Iterating completed... !!

说明:

  • 当我们执行相同的程序ConcurrentHashMap替换HashMap时,程序执行时没有任何运行时异常,例如ConcurrentModificationException
  • 但是在不同的执行点可能会有不同的输出
  • 原因:因为,当一个线程迭代 所有条目时, 它可能会 从第二个线程获得 更新的条目
  • 在上面的例子中,我们得到了更新的条目,这是可能的,因为1个第一螺纹,其迭代得到更新用2次螺纹去除
  • 下一次迭代也是如此,因为下一次可能会迭代所有条目(在这种情况下,第一个不会从第二个线程进行更新)

 

让我们也打印其他可能性

输出:

1个
2
3
4
5
6
{Key=1  Value=google.com}
Entry with {key=1 & value=google.com} is removed
Removal is done... !!
{Key=2  Value=youtube.com}
{Key=3  Value=facebook.com}
Iterating completed... !!

从上面的输出,很显然,1个第一线程没有得到更新用来自2次螺纹和1条第一线穿过的所有条目迭代

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: 这可能取决于您使用它们的情况。HashMap在读取方面通常是最快的,LinkedHashMap其次,IdentityHashMap比LinkedHashMap略慢,而ConcurrentHashMap最慢。 ### 回答2: 根据读取性能的排名,以下是四个不同的Map实现: 1. ConcurrentHashMap (并发哈希映射) ConcurrentHashMap 实现了分段锁机制,允许多个线程读取,提高并发性能。因此,在多线程环境下,ConcurrentHashMap 提供了最好的读取性能。 2. LinkedHashMap (链式哈希映射) LinkedHashMap 在内部使用双向链表维护插入顺序,该链表可以帮助保持元素的顺序,但并没有直接提升读取性能。在读取方面,LinkedHashMap 与HashMap 的性能相当。 3. HashMap (哈希映射) HashMap 是最常见的映射实现,它的读取性能在单线程环境中表现良好。它使用哈希表进行存储和检索操作,但没有提供多线程并发控制的机制。 4. IdentityHashMap (身份哈希映射) IdentityHashMap 使用了对象的引用地址作为 key 的比较依据,而不是使用对象的 equals 方法。因此,IdentityHashMap 在性能上与其他哈希映射实现相比有所不同,但与HashMap 相比,IdentityHashMap 在读取方面的性能略低。 总结起来,在单线程环境下,HashMap 和LinkedHashMap 的读取性能相当。在多线程环境下,ConcurrentHashMap 提供了最好的读取性能,而IdentityHashMap 的读取性能略低于其他哈希映射实现。 ### 回答3: HashMap、LinkedHashMap、IdentityHashMap和ConcurrentHashMap这四种数据结构在读取性能上的排名可以根据以下信息来判断。 首先是HashMap,它是基于哈希表实现的,使用键值对存储数据。它的读取性能较高,因为在查询可以通过计算哈希值快速定位到对应的位置。相对而言,HashMap在多线程环境下的性能较差。 其次是LinkedHashMap,它是HashMap的子类,在内部使用链表维护顺序。相比于HashMap,LinkedHashMap在遍历有保持插入顺序的特点,在读取操作上性能略低于HashMap,但在插入和删除操作上可以更高效地操作。 接着是IdentityHashMap,它与HashMap相比,在判断两个key是否相等不使用equals方法,而是使用"=="操作符。这导致IdentityHashMap在读取性能上相比于HashMap稍高,因为避免了equals方法的比较。 最后是ConcurrentHashMap,它是线程安全的HashMap的实现,通过使用锁机制和分段锁等手段来实现并发操作。由于采用了并发控制的机制,ConcurrentHashMap在多线程环境下能够实现较好的读取性能。 综上所述,根据读取性能的排名,可以将它们排序为:ConcurrentHashMap > HashMap > LinkedHashMap > IdentityHashMap。但需要注意的是,这只是从读取性能来看的一个相对的排名,实际的性能还受到其他因素的影响,如数据规模、线程数量等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值