Java并发编程之详解ConcurrentHashMap类

前言

由于Java程序员常用的HashMap的操作方法不是同步的,所以在多线程环境下会导致存取操作数据不一致的问题,Map接口的另一个实现类Hashtable 虽然是线程安全的,但是在多线程下执行效率很低。为了解决这个问题,在java 1.5版本中引入了线程安全的集合类ConcurrentMap。

java.util.concurrent.ConcurrentMap接口是Java集合类框架提供的线程安全的map,这意味着多线程同时访问它,不会影响map中每一条数据的一致性。ConcurrentMap接口有两个实现类ConcurrentHashMap和ConcurrentSkipListMap,经常被使用的是ConcurrentHashMap,我们来重点关注它。

一、创建ConcurrentHashMap对象

通过下面的代码创建ConcurrentHashMap

// 创建容量为8,负载系数为0.6的ConcurrentHashMap
ConcurrentHashMap<Key, Value> numbers = new ConcurrentHashMap<>(8, 0.6f);

使用上面的代码,我们创建一个叫做numbers的ConcurrentHashMap对象。

  • Key - 用于关联Map中每个元素的唯一标识
  • Value - Map中每个元素,可以通过key值获取value

需要我们特别注意的是new ConcurrentHashMap<>(8, 0.6).

  • capacity容量 - 第一个参数表示这个map的容量是8,也就是说这个对象可以存储8个键值对
  • loadFactor负载因子 - 这个map对象的负载因子是 0.6. 这意味着,每当我们的哈希表被填满60%的时候,条目就会被移动到一个新的哈希表,其容量大小是原来哈希表的两倍。

默认容量与负载因子
我们还可以通过下面的代码初始化一个ConcurrentHashMap对象,默认情况下capacity=16,loadFactor=0.75

ConcurrentHashMap<Key, Value> numbers1 = new ConcurrentHashMap<>();

二、ConcurrentHashMap常用方法

2.1. 向ConcurrentHashMap插入元素

  • put(K,V) - 向map中插入key/value 键值对数据
  • putAll(map) - 把另一个map中的所有entries插入到当前的map中
  • putIfAbsent(K,V) - 向map中插入key/value 键值对数据,如果该键值对的key在map不存在则插入数据,否则不做操作。
import java.util.concurrent.ConcurrentHashMap;

class Main {
    public static void main(String[] args) {
        // 创建ConcurrentHashMap 用于保存偶数
        ConcurrentHashMap<String, Integer> evenNumbers = new ConcurrentHashMap<>();

        // 使用put()方法插入数据
        evenNumbers.put("Two", 2);
        evenNumbers.put("Four", 4);

        // 使用putIfAbsent()插入数据
        evenNumbers.putIfAbsent("Six", 6);
        System.out.println("偶数集合ConcurrentHashMap: " + evenNumbers);

        //创建ConcurrentHashMap用于保存整数
        ConcurrentHashMap<String, Integer> numbers = new ConcurrentHashMap<>();
        numbers.put("One", 1);

        // 使用putAll()插入数据
        numbers.putAll(evenNumbers);
        System.out.println("整数集合ConcurrentHashMap: " + numbers);
    }
}

输出结果:

偶数集合ConcurrentHashMap: {Six=6, Four=4, Two=2}
整数集合ConcurrentHashMap: {Six=6, One=1, Four=-4, Two=2}

2.2.批量获取ConcurrentHashMap 元素

  • entrySet()- 获取 map中key/value 键值对集合
  • keySet()- 获取map中所有的key的集合
  • values()- 获取map中所有的value的集合
import java.util.concurrent.ConcurrentHashMap;

class Main {
    public static void main(String[] args) {
        ConcurrentHashMap<String, Integer> numbers = new ConcurrentHashMap<>();

        numbers.put("One", 1);
        numbers.put("Two", 2);
        numbers.put("Three", 3);
        System.out.println("ConcurrentHashMap: " + numbers);

        // 获取 map中key/value 键值对集合
        System.out.println("Key/Value mappings: " + numbers.entrySet());

        // 获取map中所有的key的集合
        System.out.println("Keys: " + numbers.keySet());

        // 获取map中所有的value的集合
        System.out.println("Values: " + numbers.values());
    }
}

输出结果

ConcurrentHashMap: {One=1, Two=2, Three=3}
Key/Value mappings: [One=1, Two=2, Three=3]
Keys: [One, Two, Three]
Values: [1, 2, 3]

2.3. 获取指定Key元素的value值

  • get() - 获取指定key元素的value值,如果key不存在返回null
  • getOrDefault() - 获取指定key元素的value值,如果key不存在返回一个指定的默认值
import java.util.concurrent.ConcurrentHashMap;

class Main {
    public static void main(String[] args) {

        ConcurrentHashMap<String, Integer> numbers = new ConcurrentHashMap<>();
        numbers.put("One", 1);
        numbers.put("Two", 2);
        numbers.put("Three", 3);
        System.out.println("ConcurrentHashMap: " + numbers);

        // 获取指定key元素的value值,如果key不存在返回null
        int value1 = numbers.get("Three");
        System.out.println("Using get(): " + value1);

        // 获取指定key元素的value值,如果key不存在返回一个指定的默认值
        int value2 = numbers.getOrDefault("Five", 5);
        System.out.println("Using getOrDefault(): " + value2);
    }
}

输出结果

ConcurrentHashMap: {One=1, Two=2, Three=3}
Using get(): 3
Using getOrDefault(): 5

2.4.移除ConcurrentHashMap中的元素

  • remove(key) - 根据指定的key删除map中的元素,并将该元素返回
  • remove(key, value) - 只有当map中存在指定的键映射到指定的值时,才会从map中删除条目,并返回一个布尔值。返回true表示删除成功,否则表示map中没有这个键值对。
import java.util.concurrent.ConcurrentHashMap;

class Main {
    public static void main(String[] args) {

        ConcurrentHashMap<String, Integer> numbers = new ConcurrentHashMap<>();
        numbers.put("One", 1);
        numbers.put("Two", 2);
        numbers.put("Three", 3);
        System.out.println("ConcurrentHashMap: " + numbers);

        // 根据指定的key删除map中的元素,并将该元素返回
        int value = numbers.remove("Two");
        System.out.println("Removed value: " + value);

        // 只有当map中存在指定的键映射到指定的值时,才会从map中删除条目,并返回一个布尔值。
        boolean result = numbers.remove("Three", 3);
        System.out.println("Is the entry {Three=3} removed? " + result);

        System.out.println("Updated ConcurrentHashMap: " + numbers);
    }
}

输出结果

ConcurrentHashMap: {One=1, Two=2, Three=3}
Removed value: 2
Is the entry {Three=3} removed? True
Updated ConcurrentHashMap: {One=1}

到此这篇关于Java并发编程之详解ConcurrentHashMap类的文章就介绍到这了,更多相关Java ConcurrentHashMap内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 分析Java中Map的遍历性能问题

    一.引言 我们知道java HashMap的扩容是有成本的,为了减少扩容的次数和成本,可以给HashMap设置初始容量大小,如下所示: HashMap<string, integer=""> map0 = new HashMap<string, integer="">(100000); 但是在实际使用的过程中,发现性能不但没有提升,反而显著下降了!代码里对HashMap的操作也只有遍历了,看来是遍历出了问题,于是做了一番测试,得到如下结果:

  • Java中HashMap的初始容量设置方式

    Java中HashMap的初始容量设置 根据阿里巴巴Java开发手册上建议HashMap初始化时设置已知的大小,如果不超过16个,那么设置成默认大小16: 集合初始化时, 指定集合初始值大小. 说明: HashMap使用HashMap(int initialCapacity)初始化 正例: initialCapacity = (需要存储的元素个数 / 负载因子) + 1.注意负载因子(即loader factor)默认为0.75, 如果暂时无法确定初始值大小,请设置为16(即默认值). 反例:

  • 分析Java并发编程之信号量Semaphore

    目录 一.认识Semaphore 1.1.Semaphore 的使用场景 1.2.Semaphore 使用 1.3.Semaphore 信号量的模型 二.Semaphore 深入理解 2.1.Semaphore 基本属性 2.2.Semaphore 的公平性和非公平性 2.3.其他 Semaphore 方法 一.认识Semaphore 1.1.Semaphore 的使用场景 Semaphore 的使用场景主要用于流量控制,比如数据库连接,同时使用的数据库连接会有数量限制,数据库连接不能超过一定的

  • Java 将List中的实体类按照某个字段进行分组并存放至Map中操作

    1.JDK1.8之前: 假设有实体类User,里面有字段id,我们将相同id的User进行分组,并存放在Map中.(例子不是很恰当,但很能说明问题) public static void main(String[] args) { List<User> list = new ArrayList<>(); list.add(new User(1, 1)); list.add(new User(1, 2)); list.add(new User(2, 1)); list.add(new

  • Java8 中使用Stream 让List 转 Map使用问题小结

    在使用 Java 的新特性 Collectors.toMap() 将 List 转换为 Map 时存在一些不容易发现的问题,这里总结一下备查. 空指针风险 java.lang.NullPointerException 当 List 中有 null 值的时候,使用 Collectors.toMap() 转为 Map 时,会报 java.lang.NullPointerException,如下: List<SdsTest> sdsTests = new ArrayList<>(); S

  • java中map与实体类的相互转换操作

    java中map与实体类的相互转换 1. 在 pom.xml 中引入依赖包 <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.54</version> </dependency> 2. 在控制类中引入 import com.alibaba.fastjson.JSON; 3. 类型转

  • 深入理解Java中的HashMap

    一.HashMap的结构图示 ​本文主要说的是jdk1.8版本中的实现.而1.8中HashMap是数组+链表+红黑树实现的,大概如下图所示.后面还是主要介绍Hash Map中主要的一些成员以及方法原理. ​那么上述图示中的结点Node具体类型是什么,源码如下.Node是HashMap的内部类,实现了Map.Entery接口,主要就是存放我们put方法所添加的元素.其中的next就表示这可以构成一个单向链表,这主要是通过链地址法解决发生hash冲突问题.而当桶中的元素个数超过阈值的时候就换转为红黑

  • Java源码解析之详解ImmutableMap

    一.案例场景 遇到过这样的场景,在定义一个static修饰的Map时,使用了大量的put()方法赋值,就类似这样-- public static final Map<String,String> dayMap= new HashMap<>(); static { dayMap.put("Monday","今天上英语课"); dayMap.put("Tuesday","今天上语文课"); dayMap.p

  • Java并发编程之详解ConcurrentHashMap类

    前言 由于Java程序员常用的HashMap的操作方法不是同步的,所以在多线程环境下会导致存取操作数据不一致的问题,Map接口的另一个实现类Hashtable 虽然是线程安全的,但是在多线程下执行效率很低.为了解决这个问题,在java 1.5版本中引入了线程安全的集合类ConcurrentMap. java.util.concurrent.ConcurrentMap接口是Java集合类框架提供的线程安全的map,这意味着多线程同时访问它,不会影响map中每一条数据的一致性.ConcurrentM

  • Java并发编程之详解CyclicBarrier线程同步

    CyclicBarrier线程同步 java.util.concurrent.CyclicBarrier提供了一种多线程彼此等待的同步机制,可以把它理解成一个障碍,所有先到达这个障碍的线程都将将处于等待状态,直到所有线程都到达这个障碍处,所有线程才能继续执行. 举个例子:CyclicBarrier的同步方式有点像朋友们约好了去旅游,在景点入口处集合,这个景点入口就是一个Barrier障碍,等待大家都到了才一起进入景点游览参观. 进入景点后大家去爬山,有的人爬得快,有的人爬的慢,大家约好了山顶集合

  • java多线程编程技术详解和实例代码

     java多线程编程技术详解和实例代码 1.   Java和他的API都可以使用并发. 可以指定程序包含不同的执行线程,每个线程都具有自己的方法调用堆栈和程序计数器,使得线程在与其他线程并发地执行能够共享程序范围内的资源,比如共享内存,这种能力被称为多线程编程(multithreading),在核心的C和C++语言中并不具备这种能力,尽管他们影响了JAVA的设计. 2.   线程的生命周期 新线程的生命周期从"新生"状态开始.程序启动线程前,线程一直是"新生"状态:

  • JAVA多线程编程实例详解

    本文实例讲述了JAVA多线程编程.分享给大家供大家参考,具体如下: 进程是系统进行资源调度和分配的一个独立单位. 进程的特点 独立性:进程是系统中独立存在的实体,拥有自己的独立资源和私有空间.在没有经过进程本身允许的情况下,不能直接访问其他进程. 动态性:进程与程序的区别在于,前者是一个正在系统中活动的指令,而后者仅仅是一个静态的指令集合 并发性:多个进程可以在单个处理器上并发执行,而不受影响. 并发性和并行性的区别: 并行性:在同一时刻,有多条指令在多个处理器上同时执行(多个CPU) 并发性:

  • Java网络编程基础详解

    目录 网络编程 1.1 概述 1.2.网络通信的要素 1.网络编程中有两个主要的问题 2.网络编程中的三要素 1.3.IP 1.4.端口 1.5.通信协议 UDP协议 TCP协议 1.6.TCP模拟通信 客户端 服务器 文件上传 1.7.UDP 1.8.URL 实例: 总结 网络编程 1.1 概述 1.2.网络通信的要素 如何实现网络通信? 1.网络编程中有两个主要的问题 如何定位到网络上的一台或者多台主机 使用cmd-->ping命令,ping一个域名,可以看到访问的主机的IP地址 [外链图片

  • Java并发编程ThreadLocalRandom类详解

    目录 为什么需要ThreadLocalRandom ThreadRandom原理详解 为什么需要ThreadLocalRandom java.util.Random一直都是使用比较广泛的随机数生成工具类,而且java.lang.Math中的随机数生成也是使用的java.util.Random实例. 我们下面看一下java.util.Random的使用方法: import java.util.Random; public class code_4_threadRandom { public sta

  • java并发编程_线程池的使用方法(详解)

    一.任务和执行策略之间的隐性耦合 Executor可以将任务的提交和任务的执行策略解耦 只有任务是同类型的且执行时间差别不大,才能发挥最大性能,否则,如将一些耗时长的任务和耗时短的任务放在一个线程池,除非线程池很大,否则会造成死锁等问题 1.线程饥饿死锁 类似于:将两个任务提交给一个单线程池,且两个任务之间相互依赖,一个任务等待另一个任务,则会发生死锁:表现为池不够 定义:某个任务必须等待池中其他任务的运行结果,有可能发生饥饿死锁 2.线程池大小 注意:线程池的大小还受其他的限制,如其他资源池:

  • Java并发编程:CountDownLatch与CyclicBarrier和Semaphore的实例详解

    Java并发编程:CountDownLatch与CyclicBarrier和Semaphore的实例详解 在java 1.5中,提供了一些非常有用的辅助类来帮助我们进行并发编程,比如CountDownLatch,CyclicBarrier和Semaphore,今天我们就来学习一下这三个辅助类的用法. 以下是本文目录大纲: 一.CountDownLatch用法 二.CyclicBarrier用法 三.Semaphore用法 若有不正之处请多多谅解,并欢迎批评指正. 一.CountDownLatch

  • Java并发编程(CyclicBarrier)实例详解

    Java并发编程(CyclicBarrier)实例详解 前言: 使用JAVA编写并发程序的时候,我们需要仔细去思考一下并发流程的控制,如何让各个线程之间协作完成某项工作.有时候,我们启动N个线程去做一件事情,只有当这N个线程都达到某一个临界点的时候,我们才能继续下面的工作,就是说如果这N个线程中的某一个线程先到达预先定义好的临界点,它必须等待其他N-1线程也到达这个临界点,接下来的工作才能继续,只要这N个线程中有1个线程没有到达所谓的临界点,其他线程就算抢先到达了临界点,也只能等待,只有所有这N

  • Java并发编程总结——慎用CAS详解

    一.CAS和synchronized适用场景 1.对于资源竞争较少的情况,使用synchronized同步锁进行线程阻塞和唤醒切换以及用户态内核态间的切换操作额外浪费消耗cpu资源:而CAS基于硬件实现,不需要进入内核,不需要切换线程,操作自旋几率较少,因此可以获得更高的性能. 2.对于资源竞争严重的情况,CAS自旋的概率会比较大,从而浪费更多的CPU资源,效率低于synchronized.以java.util.concurrent.atomic包中AtomicInteger类为例,其getAn

随机推荐