iis服务器助手广告广告
返回顶部
首页 > 资讯 > 后端开发 > Python >聊一聊concurrenthashmap的size方法原理
  • 768
分享到

聊一聊concurrenthashmap的size方法原理

2024-04-02 19:04:59 768人浏览 泡泡鱼

Python 官方文档:入门教程 => 点击学习

摘要

目录concurrentHashMap的size方法原理下面具体说一下这个size方法concurrenthashmap的size的思考我们首先会想到以下两种方法concurrent

concurrenthashmap的size方法原理

同上,这也是同一个面试的时候别人问的,我只是记得看过,在concurrenthashmap中会统计多次,当时就说会统计两次进行比较,人家接着问为啥。。。我傻了一下,这不是明摆着两次统计的中间有新的变化了,会导致统计不准确吗?当时也不知道说啥好,以为他有新的点,就说不知道。面试时很多问题其实冷静下来想一下,可以更进一步的,有时候其实也是怕他更进一步后下面的挖坑挖大了。

下面具体说一下这个size方法

代码就不贴了。只说原理。

众所周知,concurrenthashmap有很多歌segments,首先遍历segments将每个segment的count加起来作为整个concurrenthashMap的size。如果没有并发的情况下这自然就可以了,但这是多线程的,如果前脚统计完后脚有变化了,这就不准确了,源码中引入了,modCount和两次比较来实现size的确认。具体过程是:

1.进行第一遍遍历segments数组

将每个segemnt的count加起来作为总数,期间把每个segment的modCount加起来sum作为结果是否被修改的判断依据。

这里需要提一下modCount,这个是当segment有任何操作都会进行一次增量操作,代表的是对Segment中元素的数量造成影响的操作的次数,这个值只增不减!!!!只增不减很重要,这样就不会出现一个segment+1,导致modcount+1,而另一个segment-1,即modcount-1 ,从而在统计所有的时候modcount没有变化。

2.size操作就是遍历了两次所有的Segments

每次记录Segment的modCount值,然后将两次的modCount进行比较,如果相同,则表示期间没有发生过写入操作,就将原先遍历的结果返回,如果不相同,则把这个过程再重复做一次,如果再不相同,则就需要将所有的Segment都住,然后一个一个遍历了。

3.如果经判断发现两次统计出的modCount并不一致

那就如上所说,要重新启用全部segment加锁的方式来进行count的获取和统计了,这样在此期间每个segement都被锁住,无法进行其他操作,统计出的count自然很准确。

而之所以之所以要先不加锁进行判断,道理很明显,就是不希望因为size操作获取这么多锁,因为获取锁不光占用资源,也会影响其他线程对ConcurrentHash的使用,影响并发情况下程序执行的效率。使用锁要谨慎!

原理大概就是这样的,具体的代码可以去看源码,而且源码1.7和1.8有差别。。。有空再贴出来比较比较吧。

concurrenthashmap的size的思考

ConcurrentHashMap是通过分段锁来控制整个Map的安全性和并发性,那么ConcurrentHashMap在求size的时候是如何兼顾到性能以及安全性的呢?

我们首先会想到以下两种方法

1.获取所有的Segment锁。

这个方法是可行的,但是这会导致并发性能变差,因为你获取了所有的锁,那么别的线程将无法对该HashMap执行任何操作。

2.逐个地获取Segment。

这种方法也有问题,有可能在后面获取下一个Segment里面的元素的个数的时候,上面一个Segment里面元素的个数已经很可能改变了,因此最后累加到最后,有可能数据是错误的。

那么ConcurrentHashMap采用的是什么措施呢。源码如下所示:

java1.7以前的源码:

由于在累加count的操作的过程中之前累加过的count发生变化的几率非常小,所以ConcurrentHashMap先尝试2次不锁住Segment的方式来统计每个Segment的大小,如果在统计的过程中Segment的count发生了变化,这时候再加锁统计Segment的count。

java1.7以及1,7以后的源码:

取size的核心是sumCount函数。

    final long sumCount() {
        CounterCell[] as = counterCells; CounterCell a;
        long sum = baseCount;
        if (as != null) {
            for (int i = 0; i < as.length; ++i) {
                if ((a = as[i]) != null)
                    sum += a.value;
            }
        }
        return sum;
    }

核心逻辑:当 counterCells 不是 null,就遍历元素,并和 baseCount 累加。

查看两个属性:baseCount 和 counterCells。

先看 baseCount

private transient volatile long baseCount;

baseCount是一个 volatile 的变量,在 addCount 方法中会使用它,而 addCount 方法在 put 结束后会调用。在 addCount 方法中,会对这个变量做 CAS 加法。

  private final void addCount(long x, int check) {
        CounterCell[] as; long b, s;
        if ((as = counterCells) != null ||
            !U.compareAndSetLong(this, BASECOUNT, b = baseCount, s = b + x)) {
            CounterCell a; long v; int m;
            boolean uncontended = true;
            if (as == null || (m = as.length - 1) < 0 ||
                (a = as[ThreadLocalRandom.getProbe() & m]) == null ||
                !(uncontended =
                  U.compareAndSetLong(a, CELLVALUE, v = a.value, v + x))) {
                fullAddCount(x, uncontended);
                return;
            }
            if (check <= 1)
                return;
            s = sumCount();
        }

但是如果并发导致 CAS 失败了,怎么办呢?使用 counterCells。

如果上面 CAS 失败了,在 fullAddCount 方法中,会继续死循环操作,直到成功。

最后,再来看一下counterCells这个类。

    @jdk.internal.vm.annotation.Contended static final class CounterCell {
        volatile long value;
        CounterCell(long x) { value = x; }
    }

上述源码中的注释是为了避免伪共享(false sharing)。

先引用个伪共享的解释: 缓存系统中是以缓存行(cache line)为单位存储的。

缓存行是2的整数幂个连续字节, 一般为32-256个字节。最常见的缓存行大小是64个字节。

当多线程修改互相独立的变量时, 如果这些变量共享同一个缓存行,就会无意中影响彼此的性能,这就是伪共享。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程网。

--结束END--

本文标题: 聊一聊concurrenthashmap的size方法原理

本文链接: https://www.lsjlt.com/news/140762.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • 聊一聊concurrenthashmap的size方法原理
    目录concurrenthashmap的size方法原理下面具体说一下这个size方法concurrenthashmap的size的思考我们首先会想到以下两种方法concurrent...
    99+
    2022-11-13
  • concurrenthashmap中size方法原理的示例分析
    小编给大家分享一下concurrenthashmap中size方法原理的示例分析,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!concurrenthashmap的size方法原理同上,这也是同一个面试的时候别人问的,我只是...
    99+
    2023-06-29
  • 一文聊聊Vue-Router的实现原理
    路由既然我们在分析路由,我们首先来说说什么是路由,什么是后端路由、什么是前端路由。路由就是根据不同的 url 地址展示不同的内容或页面,早期路由的概念是在后端出现的,通过服务器端渲染后返回页面,随着页面越来越复杂,服务器端压力越来越大。后来...
    99+
    2023-05-14
    vue-router Vue
  • 深入聊一聊JS中new的原理与实现
    目录定义构造函数体不同 无返回值 返回对象 返回非对象 没有属性绑定+返回非对象 构造函数类型不同 构造函数为普通函数 构造函数为箭头函数 手写new 总结定义 new 运算符创建一...
    99+
    2022-11-12
  • 聊聊gitlab不可见的原因和解决方法
    在软件开发领域,版本控制系统是一个必不可少的工具,而Git则是其中最知名且最强大的版本控制系统之一。而在Git之上,GitLab则是一个建立在Git之上的完整的开发工具,它提供了仓库管理、Issue管理、持续集成、代码审查等多种功能,被广泛...
    99+
    2023-10-22
  • 一起来聊聊JavaScript事件循环的原理与实例
    以上就是一起来聊聊JavaScript事件循环的原理与实例的详细内容,更多请关注编程网其它相关文章!...
    99+
    2022-11-22
    前端 JavaScript
  • 简单聊聊SQL注入的原理以及一般步骤
    目录原理一般利用步骤1.判断注入点2.判断查询字段数3.查数据库名、版本号、用户名等信息4.查询表名5.查询字段名6.查询记录内容总结原理 SQL注入是一种攻击方式,在这种攻击方式中...
    99+
    2022-11-13
  • 一文聊聊Vue中provide和inject的使用方法
    Vue中如何使用provide与inject?下面本篇文章就来给大家介绍一下Vue中provide和inject的使用方法,希望对大家有所帮助!在vue2.0里面provide与inject是以选项式(配置)API的方式在组件中进行使用的,...
    99+
    2023-05-14
    Vue javascript
  • 聊聊github页面无法访问的原因和解决方法
    在如今数字化的时代,Github已经成为了开发者和程序员最喜欢的平台之一。但是,近年来许多用户报告了一些 Github 页面打不开的情况,给他们的工作和项目带来了不便。这些问题到底出在哪里呢?在这篇文章中,我们将会探讨引起 Github 页...
    99+
    2023-10-22
  • 聊聊webview不调用javascript的的原因和解决方法
    在Android开发中,Webview是一个常用的控件,用于在应用程序中嵌入网页或者其他HTML组件。然而,有时候在使用Webview的过程中,可能会遇到Webview不调用JavaScript的问题。本文将主要介绍这个问题出现的原因和解决...
    99+
    2023-05-14
  • 聊聊Vue Router跨域报错的原因和解决方法
    Vue Router 跨域报错是一个很普遍的问题。在使用 Vue Router 路由时,如果跨域访问另一个网站或 API,会出现跨域报错。本文将介绍 Vue Router 跨域报错的原因和解决方法。一、跨域的原因在浏览器中,同源策略是一种安...
    99+
    2023-05-14
  • 聊聊golang中一些常见的文本替换方法
    Golang是一个强类型、高效的编程语言,它拥有丰富的语法特性和面向对象的方法,受到越来越多程序员的欢迎。 在Golang中,替换文本是一个非常常见的操作。无论是针对字符串还是文件内容,都有很多函数和API可以实现快速高效地替换文本。本文将...
    99+
    2023-05-14
  • 聊聊jquery a标签不能点的原因及解决方法
    最近一位网友向我求助,她在自己的网站上使用 jQuery,但是页面上的 a标签不能被点击。这是一个比较常见的问题,但也有几种原因导致它发生。以下是可能出现的原因及解决方法:样式覆盖问题有时候,你可能会因为 CSS 样式问题导致 a 标签不能...
    99+
    2023-05-14
  • 聊聊php判断一个数组是否为空的方法
    PHP是一种广泛使用的开源脚本语言,许多网站都使用PHP作为后端语言。在PHP中,处理数组是一项基本任务,其中判断一个数组是否为空是一个非常常见的需求。本文将介绍如何使用PHP语言来判断一个数组是否为空。首先,让我们看一下如何创建一个空数组...
    99+
    2023-05-14
  • 深入聊聊Node进程管理工具-pm2的使用方法
    如何使用Node进程管理工具-pm2,下面本篇文章带大家聊聊Node进程管理工具-pm2的使用方法,希望对大家有所帮助!pm2 是什么pm2 是一个守护进程管理工具,它能帮你守护和管理你的应用程序。通常一般会在服务上线的时候使用 pm2 进...
    99+
    2023-05-14
    node Node.js
  • 夯实Java基础系列4:一文了解final关键字的特性、使用方法,以及实现原理
    本系列文章将整理到我在GitHub上的《Java面试指南》仓库,更多精彩内容请到我的仓库里查看https://github.com/h3pl/Java-Tutorial喜欢的话麻烦点下Star哈文章首发于我的个人博客:www.how2pla...
    99+
    2023-06-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作