代码证明HashMap和ConcurrentHashMap线程的安全问题

文章讨论了HashMap在多线程环境下的线程不安全性,解释了可能出现的数据丢失问题,并介绍了ConcurrentHashMap如何通过分段锁、volatile和原子性操作保证线程安全。通过代码示例展示了在并发插入时HashMap可能出现的数据不一致性和ConcurrentHashMap的正确行为。
摘要由CSDN通过智能技术生成

1、HashMap线程不安全

HashMap在内部实现上采用了数组+链表/红黑树的方式来存储数据。在多线程的情况下,如果两个线程同时对同一个桶进行操作(如添加元素),就有可能会导致链表的链断或者元素被覆盖的问题,最终导致数据的不一致性。这是因为HashMap在没有同步措施的情况下,不保证线程安全,即多个线程同时访问同一个桶的时候,可能会出现竞争条件,从而导致数据不一致。

具体来说,当两个线程同时向同一个桶中添加元素时,可能会出现以下情况:

  1. 线程A读取桶中的元素,此时桶中只有一个元素,即key1-value1。
  2. 线程B也读取桶中的元素,此时桶中还是只有一个元素。
  3. 线程A向桶中添加一个新元素key2-value2,这时候key2与key1的hashCode()可能相同,导致两个元素在同一个桶中。
  4. 线程B也向桶中添加一个新元素key3-value3,同样也可能会被放在同一个桶中。
  5. 当线程A和线程B都执行完毕后,桶中可能会出现以下两种情况:
    • key1-value1和key2-value2都在同一个链表中,而key3-value3没有被添加到桶中,导致数据丢失。
    • key1-value1和key3-value3都在同一个链表中,而key2-value2没有被添加到桶中,导致数据丢失。

因此,为了避免这种情况的发生,需要使用线程安全的Map实现,如ConcurrentHashMap,或者在使用HashMap时使用同步措施,如使用synchronized关键字或者使用并发包中的锁来保证线程安全。

2、ConcurrentHashMap线程安全

ConcurrentHashMap是Java中的一个线程安全的哈希表实现,它提供了高效的并发访问和修改操作。它的线程安全性主要基于以下几个方面:

  • 分段锁机制

ConcurrentHashMap内部采用了分段锁的机制,也就是将整个哈希表分成多个小的哈希表段,每个段都可以独立地进行加锁和解锁操作。这样就可以同时进行多个线程的读写操作,而不必等待其他线程释放锁,从而提高了并发性能。

  • volatile修饰的元素数组

ConcurrentHashMap内部使用了一个volatile修饰的元素数组,它可以保证多个线程对数组元素的读写操作的可见性,从而保证了多线程环境下数据的一致性和安全性。

  • 原子性操作

ConcurrentHashMap内部使用了一些原子性操作,比如CAS操作和volatile修饰的变量等,来保证对哈希表中的元素进行原子性的读写操作,从而避免了多线程环境下的数据竞争和不一致性问题。

3、 代码实际证明

3.1 HashMap

public class HashMapDemo implements Runnable {
private static final HashMap<String, Integer> map = new HashMap<>();

@Override
public void run() {
    for (int i = 0; i < 10000; i++) {
          map.put("key" + i + new Random(10), i);
    }
}

public static void main(String[] args) throws InterruptedException {
    Runnable runnable = new HashMapDemo();
    Thread t1 = new Thread(runnable);
    Thread t2 = new Thread(runnable);
    t1.start();
    t2.start();
    t1.join();
    t2.join();
    System.out.println("HashMap size: " + map.size());
}
}

 在上面的代码中,我们创建了一个包含10000个元素的HashMap,并启动了两个线程同时向其中添加元素。由于HashMap没有进行同步措施,因此在运行过程中可能会出现数据不一致的情况。当运行结束后,我们输出HashMap的大小,如果数据没有出现不一致性的问题,那么HashMap的大小应该为20000。但是,由于HashMap不是线程安全的,因此在某些情况下,运行结果可能会小于20000,说明出现了数据不一致性的问题。

3.2  ConcurrentHashMap

public class ConcurrentHashMapDemo implements Runnable {
private static final ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();

@Override
public void run() {
    for (int i = 0; i < 10000; i++) {
        map.put("key" + i + new Random(10), i);
    }
}

public static void main(String[] args) throws InterruptedException {
    Runnable runnable = new ConcurrentHashMapDemo();
    Thread t1 = new Thread(runnable);
    Thread t2 = new Thread(runnable);
    t1.start();
    t2.start();
    t1.join();
    t2.join();
    System.out.println("ConcurrentHashMap size: " + map.size());
}
}

在上面的代码中,我们使用了ConcurrentHashMap代替了HashMap,并且两个线程同时向其中添加元素。由于ConcurrentHashMap是线程安全的,因此在运行过程中不会出现数据不一致性的问题。当运行结束后,我们输出ConcurrentHashMap的大小,应该为20000

这就是用代码直接证明 ConcurrentHashMap 和 HashMap的线程安全问题

HashMapConcurrentHashMap的区别在于线程安全性和并发性能。 HashMap是非线程安全的,如果多个线程同时对其进行读操作,可能会导致数据不一致或者抛出异常。而ConcurrentHashMap线程安全的,可以在多线程环境下使用而不需要额外的同步措施。 ConcurrentHashMap保证线程安全的方式是通过分段锁技术。它将数据分成多个段(Segment),每个段都有自己的锁。当一个线程对某个段进行操作时,只需获取该段的锁,不会影响其他段的访问。这样,不同的线程可以同时操作不同的段,提高了并发性能。而HashMap没有这种机制,所有线程都需要竞争同一个锁,因此并发性能较低。 此外,ConcurrentHashMap还使用了CAS(Compare and Swap)操作和volatile关键字来保证数据的一致性。CAS操作是一种无锁的原子操作,它可以确保对变量的读操作是原子的。volatile关键字可以保证变量的可见性,即当一个线程修改了变量的值后,其他线程可以立即看到这个修改。 综上所述,ConcurrentHashMap相较于HashMap,具有更好的线程安全性和并发性能,适用于高并发环境下的操作。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [阿里面试题:ConcurrentHashMap为什么是线程安全的?](https://download.csdn.net/download/weixin_38717171/14854002)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [HashMapconcurrentHashMap的区别](https://blog.csdn.net/qq_42949615/article/details/124109063)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [HashMapConcurrentHashMap的区别,ConcurrentHashMap线程安全吗,ConcurrentHashMap如何保证线程安全?](https://blog.csdn.net/YANG_Gang2017/article/details/80218091)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一枝风

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值