ConcurrentHashMap同时读取和更新

本文通过示例展示了在多线程环境下,HashMap的迭代与修改操作会导致ConcurrentModificationException,而ConcurrentHashMap则能避免这种异常,支持并发读写。对比了两者在并发控制上的差异,强调了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条第一线穿过的所有条目迭代

评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值