Java ConcurrentHashMap

ConcurrentHashMap 实现了一个线程安全且保证原子操作的高性能的 Map。在本教程中,我们将通过示例介绍 Java ConcurrentHashMap 类和它的用法。

在本教程中,我们将通过示例介绍 Java ConcurrentHashMap 类和它的用法。

ConcurrentHashMap 实现了一个线程安全且保证原子操作的高性能的 Map。ConcurrentHashMap 实现了 ConcurrentMap 接口,适用于多线程并发环境。

Java ConcurrentHashMap 类实现了 ConcurrentMap 接口。

创建 ConcurrentHashMap

我们导入 java.util.ConcurrentHashMap 类后,就可以通过构造方法创建 ConcurrentHashMap 对象了。

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

ConcurrentHashMap 的底层实现和 HashMap 相似,只是通过 CAS 操作和枷锁机制实现多线程下的安全操作。

因此,ConcurrentHashMap 的构造方法也支持传入初始容量和负载因子,如下:

ConcurrentHashMap(int initialCapacity, float loadFactor)

关于初始容量和负载因子参数的更多介绍请查看 Java HashMap 类

从其他 Map 创建 ConcurrentHashMap

下面示例展示了如何使用构造方法 ConcurrentHashMap(Map<? extends K, ? extends V> m) 将其他的 Map 对象转为 ConcurrentHashMap。

import java.util.concurrent.ConcurrentHashMap;
import java.util.HashMap;

public class Main {
  public static void main(String[] args) {
    HashMap<String, Integer> evenNumbers = new HashMap<>();
    evenNumbers.put("Two", 2);
    evenNumbers.put("Four", 4);
    System.out.println("HashMap: " + evenNumbers);

    ConcurrentHashMap<String, Integer> numbers = new ConcurrentHashMap<>(evenNumbers);
    numbers.put("Three", 3);
    System.out.println("ConcurrentHashMap: " + numbers);
  }
}

输出

HashMap: {Four=4, Two=2}
ConcurrentHashMap: {Four=4, Two=2, Three=3}

ConcurrentHashMap 的方法

ConcurrentHashMap 类实现了 ConcurrentHashMap 接口和 Map 接口定义的全部的方法。我们看一下常用的操作:

向 ConcurrentHashMap 插入元素

  • put() - 将指定的键值对条目插入到 Map 中
  • putAll() - 将指定 Map 中的所有条目插入到 Map 中
  • putIfAbsent() - 如果 Map 中不存在指定的键,则将指定的键值对条目插入到 Map 中

例如,

import java.util.concurrent.ConcurrentHashMap;

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

    // put()
    evenNumbers.put("Two", 2);
    evenNumbers.put("Four", 4);

    // putIfAbsent()
    evenNumbers.putIfAbsent("Six", 6);
    System.out.println("ConcurrentHashMap of even numbers: " + evenNumbers);

    ConcurrentHashMap<String, Integer> numbers = new ConcurrentHashMap<>();
    numbers.put("One", 1);

    // putAll()
    numbers.putAll(evenNumbers);
    System.out.println("ConcurrentHashMap of numbers: " + numbers);
  }
}

输出

ConcurrentHashMap of even numbers: {Six=6, Four=4, Two=2}
ConcurrentHashMap of numbers: {Six=6, One=1, Four=4, Two=2}

entrySet()、keySet() 和 values()

  • entrySet() - 返回 Map 的所有键值对条目的 Set 集合
  • keySet() - 返回 Map 的所有键的 Set 集合
  • values() - 返回 Map 的所有值的集合

例如,

import java.util.concurrent.ConcurrentHashMap;

public 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);

    // Using entrySet()
    System.out.println("Key/Value mappings: " + numbers.entrySet());

    // Using keySet()
    System.out.println("Keys: " + numbers.keySet());

    // Using values()
    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]

使用 get() 和 getOrDefault()

  • get() - 返回与指定键关联的值。如果未找指定键,则返回 null
  • getOrDefault() - 返回与指定键关联的值。如果未找到该键,则返回指定的默认值。

例如,

import java.util.concurrent.ConcurrentHashMap;

public 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);

    // get()
    int value1 = numbers.get("Three");
    System.out.println("Using get(): " + value1);

    // getOrDefault()
    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

删除 ConcurrentHashMap 元素

  • remove(key) - 删除并返回与指定键关联的条目
  • remove(key, value) - 删除与指定键和指定值匹配的条目,成功返回 true,否则返回 false

例如,

import java.util.concurrent.ConcurrentHashMap;

public 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);

    int value = numbers.remove("Two");
    System.out.println("Removed value: " + value);

    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}

ConcurrentHashMap 并行操作

ConcurrentHashMap 类提供了几个线程安全的方法可进行并行批量操作。

forEach() 方法

forEach() 方法可以并发迭代我们的条目并执行指定的操作。它有 2 个重载方法:

forEach(parallelismThreshold, transformer, action)
forEach(parallelismThreshold, action)

以下是参数说明:

  • parallelismThreshold - 并行阈值,迭代元素的并发数。
  • transformer: 转换方法。对键和值执行转换操作,并返回一个值。
  • action: 操作方法。当存在 transformer 时,action 需要一个参数为转换方法的返回值;当不存在 transformer 时,action 需要两个参数:键和值。

例如,

import java.util.concurrent.ConcurrentHashMap;

public 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);

    // transformer
    numbers.forEach(4, (k, v) -> System.out.println("key: " + k + " value: " + v));

    // no transformer
    System.out.print("Values are ");
    numbers.forEach(4, (k, v) -> v, (v) -> System.out.print(v + ", "));
  }
}

输出

ConcurrentHashMap: {One=1, Two=2, Three=3}
key: One value: 1
key: Two value: 2
key: Three value: 3
Values are 1, 2, 3,

在上面的程序中,我们使用了并行阈值 4。这意味这同时最多有 4 个元素被并行处理。

forEach() 方法的变体

  • forEachEntry() - 为每个 Entry 条目执行指定的操作
  • forEachKey() - 为每个键执行指定的操作
  • forEachValue() - 为每个值执行指定的操作

2.search()方法

search(parallelismThreshold, searchFunction) 逐个对每个键值对执行执行的搜索函数,直到搜索函数返回一个非 null 的结果,然后返回此结果,否则就返回 null

以下是参数说明:

  • parallelismThreshold - 并行阈值,迭代元素的并发数。
  • searchFunction - 搜索函数,返回非 null 值为搜索成功。

例如,

import java.util.concurrent.ConcurrentHashMap;

public 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);

    // Using search()
    String key = numbers.search(4, (k, v) -> v == 3 ? k : null);
    System.out.println("Searched value: " + key);
  }
}

输出

ConcurrentHashMap: {One=1, Two=2, Three=3}
Searched value: Three

search() 方法的变体

  • searchEntries() - 搜索功能应每个 Entry 条目
  • searchKeys() - 仅搜索键
  • searchValues() - 仅搜索值

3.reduce()方法

reduce(parallelismThreshold, transformer, reducer) 先对每个键值对执行指定的转换函数,并使用 reducer 累加所有转换的结果。

以下是参数说明:

  • parallelismThreshold - 并行阈值,迭代元素的并发数。
  • transformer: 转换方法。对键和值执行转换操作,并返回一个值。
  • reducer: 累计方法

例如,

import java.util.concurrent.ConcurrentHashMap;

public 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);

    // Using reduce()
    int sum = numbers.reduce(4, (k, v) -> v, (v1, v2) -> v1 + v2);
    System.out.println("Sum of all values: " + sum);
  }
}

输出

ConcurrentHashMap: {One=1, Two=2, Three=3}
Sum of all values: 6

在上面的程序中,注意以下语句:

numbers.reduce(4, (k, v) -> v, (v1, v2) -> v1+v2);

这里,

  • 4 是并行阈值
  • (k, v) -> v 是 lambda 表达式的转换函数,只返回了键值对中的值。
  • (v1, v2) -> v1 + v2 是一个 reducer 函数,它将所有值收集在一起并累加。

reduce() 方法的变体

  • reduceEntries() - 对所有的 Entry 对象执行 reduce 操作
  • reduceKeys() - 对所有的键执行 reduce 操作
  • reduceValues() - 对所有的值执行 reduce 操作

ConcurrentHashMap 与 HashMap

以下是 ConcurrentHashMapHashMap 之间的一些差异,

  • ConcurrentHashMap 是一个线程安全的集合,即多个线程可以同时访问和修改它,但不会有线程安全问题。
  • ConcurrentHashMap 提供了可并发批量操作的方法,如 forEach()search()reduce()
  • 在多线程环境下,ConcurrentHashMap 是高性能的。