当前位置:Java -> ConcurrentHashMap中的并行性
ConcurrentHashMap
在多线程应用程序中被广泛使用。多线程应用程序的示例包括在线游戏应用程序和聊天应用程序,这些应用程序为应用程序增加了并发性的优势。为了使应用程序在性质上更具并发性,ConcurrentHashMap
引入了一个称为“并行性”的概念。
在本文中,我们将更多地学习关于 Concurrent Hashmaps 中的并行性。
基本上,并行计算将一个问题划分为子问题,通过并行地解决这些子问题,最终将子问题的结果合并起来。在这里,子问题将在独立的线程中运行。
ConcurrentHashMap
的并行性支持为了利用 ConcurrentHashMap
中的并行性,我们需要使用Java 1.8版本及更高版本。并行性不支持低于1.8版本的Java。
Java 引入了一个名为“fork and join”的框架,它将实现并行计算。它利用 java.util.concurrent.ForkJoinPool API 来实现并行计算。这个 API 被用来在 ConcurrentHashMap
中实现并行性。
ConcurrentHashMap
中的并行方法ConcurrentHashMap
通过并行计算有效地使用并行性,其中使用了并行性阈值。它是一个数值,其默认值为2。
以下是具有并行性能力的方法:
forEach()
reduce()
reduceEntries()
forEachEntry()
forEachKey()
forEachValue()
ConcurrentHashMap
处理并行性的方式略有不同,如果查看上述方法的参数,您将能理解这一点。这些方法中的每一个都可以将并行性阈值作为参数。
首先,并行性是一个可选功能。通过在代码中添加适当的并行阈值值,我们可以启用此功能。
ConcurrentHashMap
让我们举一个替换 concurrenthashmap
中所有字符串值的示例。这是在不使用并行性的情况下完成的。
示例:
concurrentHashMap.forEach((k,v) -> v=””);
这非常简单,我们正在遍历 concurrenthashmap
中的所有条目,并将值替换为一个空字符串。在这种情况下,我们没有使用并行性。
ConcurrentHashMap
示例:
concurrentHashMap.forEach(2, (k,v) -> v=””);
上面的例子遍历一个 ConcurrentHashMap
并用空字符串替换地图的值。forEach()
方法的参数是并行性阈值和一个功能接口。在这种情况下,问题将被划分为子问题。
问题是将并发哈希映射的值替换为一个空字符串。这通过将该问题划分为子问题实现,即为子问题创建单独的线程,并且每个线程将专注于替换值为一个空字符串。
当并行性阈值被启用时,JVM 将创建线程,每个线程将运行以解决问题并合并所有线程的结果。这个值的重要性在于,如果记录的数量达到一定水平(阈值),那么只有在上述示例中 JVM 才会启用并行处理。如果在哈希映射中有多于一个记录,则应用程序将启用并行处理。
这是一个很棒的功能;我们可以通过调整阈值来控制并行性。通过这种方式,我们可以利用应用程序中的并行处理。
请看下面的另一个例子:
concurrentHashMap.forEach(10000, (k,v) -> v=””);
在这种情况下,并行性阈值为10,000,这意味着如果记录的数量少于10,000,JVM 将不会在替换值为一个空字符串时启用并行性。
图:没有并行性的完整代码示例
图:具有并行性的完整代码示例
在上述示例中,并行性阈值为10,000。
以下代码将使用空字符串替换映射中的所有值。 这个concurrenthash
映射包含了超过10万条目。 让我们比较下面的代码在没有并行处理和启用并行处理的情况下的性能。
图:有无并行处理时代码的性能比较
运行上述代码之后,可以看到在常规forEach
操作的情况下有一点性能提升。
无并行处理所需时间->20毫秒
有并行处理所需时间->30毫秒
这是因为映射中的记录数量相当少。
但是如果我们给映射添加了1000万条记录,那么并行处理真的会赢! 这需要更少的时间处理数据。看看下面图片中的代码:
图:有无并行处理时代码的性能阈值
上面的代码将不使用并行处理将concurrenthashmap
中的所有值替换为空字符串。 接下来,它使用并行处理将concurrenthashmap
的所有值替换为字符串one。 这是输出:
无并行处理所需时间->537毫秒
有并行处理所需时间->231毫秒
可以看到,在并行处理的情况下,只需要一半的时间。
注意:上述数值不是固定的。 在不同系统中可能会产生不同的结果。
JVM使用ForkJoinPool框架在我们启用代码中的并行处理时,启用并行处理。 该框架根据当前处理的需求创建了一些工作线程。 让我们通过fastthread.io工具来查看启用并行处理时的线程转储分析。
图:fastThread报告显示了启用并行处理时的线程数
图:fastThread报告显示了启用并行处理时相同堆栈跟踪
你可以从上面的图片中了解到它正在使用更多的线程。
线程过多的原因是它正在使用ForkJoinPool API。 这是负责在幕后实现“并行处理”的API。 当你看下一节时就会理解这一点的不同之处。
让我们了解一下没有启用并行处理时的线程转储分析。
图:fastThread报告显示了未启用并行处理时的线程数
图:fastThread报告显示未启用并行性的相同堆栈跟踪
如果您仔细查看上面的图片,您会发现只使用了少数线程。在这种情况下,与上一张图片相比,只有35个线程。这种情况下有32个可运行的线程。但是,等待和定时等待线程分别为2和1。在这种情况下,可运行线程数量减少的原因是它没有调用ForkJoinPool API。
这样,fastthread.io工具可以非常智能地深入了解线程转储的内部。
我们着重研究了 concurrenthashmap
中的并行性以及如何在应用程序中使用此功能。此外,我们了解了在启用此功能时JVM会发生什么。并行性是一项很酷的功能,可以在现代并发应用程序中很好地使用。
推荐阅读: 20.常用HTTP状态码
本文链接: ConcurrentHashMap中的并行性