Java-多线程-深入理解ConcurrentHashMap

这篇具有很好参考价值的文章主要介绍了Java-多线程-深入理解ConcurrentHashMap。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

Java-多线程-深入理解ConcurrentHashMap,# java相关,java,开发语言,数据结构

什么是ConcurrentHashMap?

    ConcurrentHashMap(Concurrent:并存的,同时发生的;)
    ConcurrentHashMap是Java中的一个线程安全的哈希表实现,它可以在多线程环境下高效地进行并发操作。

为什么有ConcurrentHashMap?

    HashMap线程不安全,在多线程操作下可能会导致数据错乱

和HashMap区别

Java-多线程-深入理解ConcurrentHashMap,# java相关,java,开发语言,数据结构

示例代码对比

    使用HashMap和ConcurrentHashMap分别实现以下需求。
        用30个线程向实例化出的map中插入key,value。每一次插入后,把map打印出来(for循环中sout)
        key为for循环中的i值
        value:使用UUID

public class HashMapUnsafeTest {
    public static void main(String[] args) throws InterruptedException {
        //演示HashMap
        Map<String, String> map = new HashMap<>();
        for (int i = 0; i < 30; i++) {
            String key = String.valueOf(i);
            new Thread(() -> {
                //向集合添加内容
                map.put(key, UUID.randomUUID().toString().substring(0, 8));
                //从集合中获取内容
                System.out.println(map);
            }, "").start();
        }
    }
}
public class ConcurrentHashMapSafe {
    public static void main(String[] args) throws InterruptedException {

        //演示ConcurrentHashMap
        Map<String, String> map = new ConcurrentHashMap<>();
        for (int i = 0; i < 30; i++) {
            String key = String.valueOf(i);
            new Thread(() -> {
                //向集合添加内容
                map.put(key, UUID.randomUUID().toString().substring(0, 8));
                //从集合中获取内容
                System.out.println(map);
            }, "").start();

        }
    }
}

    多个线程同时对同一个集合进行增删操作导致错误

Java-多线程-深入理解ConcurrentHashMap,# java相关,java,开发语言,数据结构

JDK7和JDK8中ConcurrentHashMap整体架构的区别

JDK7中

Java-多线程-深入理解ConcurrentHashMap,# java相关,java,开发语言,数据结构

JDK8中

Java-多线程-深入理解ConcurrentHashMap,# java相关,java,开发语言,数据结构

使用到的数据结构:数组、单向链表、红黑树
Java-多线程-深入理解ConcurrentHashMap,# java相关,java,开发语言,数据结构

其中涉及到几个核心的参数

// 最大容量,2^30
private static final int MAXIMUM_CAPACITY = 1 << 30;
// 默认长度
private static final int DEFAULT_CAPACITY = 16;


//遗留问题:为什么树化条件是8而取消树化条件却是6呢?
// 链表树化条件-是根据线程竞争情况和红黑树的操作成本进行设计的。
static final int TREEIFY_THRESHOLD = 8;
// 取消树化条件-为了避免过度的树化,防止内存占用过高。
static final int UNTREEIFY_THRESHOLD = 6;              //链表结构中,每个节点只需要存储指向下一个节点的指针,而不需要存储节点的值。因此,链表只需要存储节点的引用,占用较少的内存空间。树结构中每个节点需要存储节点的值以及指向子节点的指针。

    核心为Hash表,存在Hash冲突问题
    使用链式存储方式解决冲突,冲突较多时,导致链表过长,查询效率较低,在JDK1.8中引入红黑树机制;
    当数组长度大于64,且链表长度大于等于8时,单项链表转为红黑树
    当链表长度小于6时,红黑树会退化为单向链表

ConcurrentHashMap的基本功能

    本质上是一个HashMap,功能和HashMap是一样的
    但ConcurrentHashMap在HashMap基础上提供了并发安全的实现,主要通过对node节点加锁实现,来保证对数据更新的安全性
锁粒度变小
Java-多线程-深入理解ConcurrentHashMap,# java相关,java,开发语言,数据结构

在性能方面的优化

    在JDK1.8中锁的粒度是数组中的某一个节点,在JDK1.7中锁定的是一个Segment,锁的范围更大
    保证线程安全机制:
        JDK7采用segment的分段锁机制实现线程安全,其中segment继承自ReentrantLock。
        JDK8采用CAS(读)+Synchronized(写)保证线程安全。

    锁的粒度:原来是对需要进行数据操作的Segment加锁,JDK8调整为对每个数组元素加锁(Node)。

    链表转化为红黑树:定位结点的hash算法简化会带来弊端,Hash冲突加剧,因此在链表节点数量大于8时,会将链表转化为红黑树进行存储。

使用到的技术-CAS

概念说明

CAS的全称是:比较并交换(Compare And Swap)。在CAS中,有这样三个值:
    V:要更新的变量(var)
    E:预期值(expected)
    N:新值(new)

比较并交换的过程如下:

    判断V是否等于E,如果等于,将V的值设置为N;如果不等,说明已经有其它线程更新了V,则当前线程放弃更新,什么都不做。
Java-多线程-深入理解ConcurrentHashMap,# java相关,java,开发语言,数据结构

举例说明

    如果有一个多个线程共享的变量i原本等于5,我现在在线程A中,想把它设置为新的值6;
我们使用CAS来做这个事情;
    首先我们用i去与5对比,发现它等于5,说明没有被其它线程改过,那我就把它设置为新的值6,此次CAS成功,i的值被设置成了6;
    如果不等于5,说明i被其它线程改过了(比如现在i的值为2),那么我就什么也不做,此次CAS失败,i的值仍然为2。

底层原理

    unsafe类——以下是类中涉及到的三个方法用来实现CAS效果的,这三个方法都是由native进行修饰的。具体的实现是由C++写的。
Java-多线程-深入理解ConcurrentHashMap,# java相关,java,开发语言,数据结构

代码演示

没有使用CAS的代码

package com.example.threadpool.CAS;



public class NoCASDemo {

    private static int counter = 0;

    public static void main(String[] args) throws InterruptedException {
        //线程一
        Thread thread1= new Thread(() -> {
            for (int i=0; i<10000;i++){
                counter++;
            }
        });
        //线程二
        Thread thread2= new Thread(() -> {
            for (int i=0; i<10000;i++){
                counter++;
            }
        });
        //执行线程
        thread1.start();
        thread2.start();
        //等待执行完线程1和2
        thread1.join();
        thread2.join();

        System.out.println("查看counter的总数"+counter);
    }

}

使用CAS的代码

package com.example.threadpool.CAS;

import java.util.concurrent.atomic.AtomicInteger;



public class CASDemo {

    private static AtomicInteger counter = new AtomicInteger(0);

    public static void main(String[] args) throws InterruptedException {
        //线程一
        Thread thread1= new Thread(() -> {
            for (int i=0; i<10000;i++){
                increment();
            }
        });
        //线程二
        Thread thread2= new Thread(() -> {
            for (int i=0; i<10000;i++){
                increment();
            }
        });
        //执行线程
        thread1.start();
        thread2.start();
        //等待执行完线程1和2
        thread1.join();
        thread2.join();

        System.out.println("查看counter的总数"+counter.get());
    }

    public static void increment() {
        int currentValue;
        int newValue;
        do {
            //获取counter对象的value值
            currentValue = counter.get();
            //将counter对象的value值加1
            newValue = currentValue + 1;

        } while (!counter.compareAndSet(currentValue, newValue));
    }

}

总结

    总的来说,ConcurrentHashMap是Java中线程安全的哈希表实现,它通过使用锁分段技术来提供高效的并发性能。相比于Hashtable,ConcurrentHashMap在多线程环境下能够更好地支持高并发读写操作。

    使用ConcurrentHashMap可以在多线程环境下安全地进行数据操作,而无需手动加锁。它通过将整个数据结构分成多个段来实现并发性能的提升,不同的线程可以同时访问不同的段,从而减少了线程之间的竞争。

    ConcurrentHashMap的设计考虑了线程安全和性能的平衡。它提供了一些有用的方法,如putIfAbsent()、replace()等,可以方便地进行原子性的操作。此外,ConcurrentHashMap还支持遍历操作,可以通过迭代器安全地遍历其中的元素。

    需要注意的是,虽然ConcurrentHashMap是线程安全的,但并不保证对于单个操作的原子性。如果需要进行复合操作,仍然需要额外的同步措施。

    总的来说,ConcurrentHashMap是一个强大的线程安全的哈希表实现,适用于多线程环境下的高并发读写操作。它提供了高效的并发性能,可以提升系统的吞吐量和响应速度。文章来源地址https://www.toymoban.com/news/detail-632881.html

到了这里,关于Java-多线程-深入理解ConcurrentHashMap的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包赞助服务器费用

相关文章

  • 【多线程系列-03】深入理解java中线程的生命周期,任务调度

    【多线程系列-03】深入理解java中线程的生命周期,任务调度

    多线程系列整体栏目 内容 链接地址 【一】深入理解进程、线程和CPU之间的关系 https://blog.csdn.net/zhenghuishengq/article/details/131714191 【二】java创建线程的方式到底有几种?(详解) https://blog.csdn.net/zhenghuishengq/article/details/127968166 【三】深入理解java中线程的生命周期,任务调度 ht

    2024年02月17日
    浏览(15)
  • 深入理解 Java 多线程、Lambda 表达式及线程安全最佳实践

    线程使程序能够通过同时执行多个任务而更有效地运行。 线程可用于在不中断主程序的情况下在后台执行复杂的任务。 创建线程 有两种创建线程的方式。 扩展Thread类 可以通过扩展Thread类并覆盖其run()方法来创建线程: 实现Runnable接口 另一种创建线程的方式是实现Runnable接口

    2024年03月15日
    浏览(17)
  • 深入理解Java线程池ThreadPoolExcutor实现原理、数据结构和算法(源码解析)

    深入理解Java线程池ThreadPoolExcutor实现原理、数据结构和算法(源码解析)

    什么是线程池?         线程池主要是为了解决执行新任务执行时,应用程序为减少为任务创建一个新线程和任务执行完毕时销毁线程所带来的开销。通过线程池,可以在项目初始化时就创建一个线程集合,然后在需要执行新任务时重用这些线程而不是每次都新建一个线

    2024年02月07日
    浏览(11)
  • 华为云出品《深入理解高并发编程:Java线程池核心技术》电子书发布

    华为云出品《深入理解高并发编程:Java线程池核心技术》电子书发布

    系统拆解线程池核心源码的开源小册 透过源码看清线程池背后的设计和思路 详细解析AQS并发工具类 点击下方链接进入官网,右上角搜索框搜索“《深入理解高并发编程:Java线程池核心技术》” 即可获取下载。 https://auth.huaweicloud.com/authui/login.html?locale=zh-cnservice=https%3A%2F%2F

    2024年02月16日
    浏览(27)
  • Java进阶(ConcurrentHashMap)——面试时ConcurrentHashMap常见问题解读 & 结合源码分析 & 多线程CAS比较并交换 初识

    Java进阶(ConcurrentHashMap)——面试时ConcurrentHashMap常见问题解读 & 结合源码分析 & 多线程CAS比较并交换 初识

    List、Set、HashMap作为Java中常用的集合,需要深入认识其原理和特性。 本篇博客介绍常见的关于Java中线程安全的ConcurrentHashMap集合的面试问题,结合源码分析题目背后的知识点。 关于List的博客文章如下: Java进阶(List)——面试时List常见问题解读 结合源码分析 关于的Set的博

    2024年02月06日
    浏览(10)
  • 大聪明教你学Java | 深入浅出聊 ConcurrentHashMap

    🍊作者简介: 不肯过江东丶,一个来自二线城市的程序员,致力于用“猥琐”办法解决繁琐问题,让复杂的问题变得通俗易懂。 🍊支持作者: 点赞👍、关注💖、留言💌~ 在 Java 的集合框架中,HashMap 是一种非常常用的数据结构,它提供了键值对形式的存储和访问方式。然

    2024年02月10日
    浏览(17)
  • 深入理解多线程编程

    深入理解多线程编程

    title: 深入理解多线程编程 date: 2024/4/25 17:32:02 updated: 2024/4/25 17:32:02 categories: 后端开发 tags: 线程同步 互斥锁 死锁避免 竞态条件 线程池 异步编程 性能优化 线程 :在操作系统中,一个程序可以被划分为多个执行流,每个执行流就是一个独立的线程。线程是进程中的一个执行实

    2024年04月25日
    浏览(11)
  • 深入理解Flink Mailbox线程模型

    Mailbox线程模型通过引入阻塞队列配合一个Mailbox线程的方式,可以轻松修改StreamTask内部状态的修改。Checkpoint、ProcessingTime Timer的相关操作(Runnable任务),会以Mail的形式保存到Mailbox内的阻塞队列中。StreamTask在invoke阶段的runMailboxLoop时期,就会轮询Mailbox来处理队列中保存的M

    2024年02月12日
    浏览(14)
  • 深入理解MySQL InnoDB线程模型

    当我们谈论数据库性能时,存储引擎的线程模型是一个不可忽视的方面。MySQL的InnoDB存储引擎,作为目前最受欢迎的存储引擎之一,其线程模型的设计对于实现高并发、高性能的数据操作至关重要。在本文中,我们将深入探讨MySQL InnoDB线程模型的工作原理和关键组件。 在Inn

    2024年01月25日
    浏览(8)
  • 深入理解高并发编程 - 线程的执行顺序

    在Java中,线程的执行顺序是由操作系统的调度机制决定的,具体顺序是不确定的,取决于多个因素,如操作系统的调度策略、线程的优先级、线程的状态转换等。因此,不能对线程的执行顺序做出可靠的假设。 以下是一个简单的Java代码示例,演示了多个线程的执行顺序是不

    2024年02月14日
    浏览(16)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包