java并发:多个写入器,一个读取器

2023-12-30

我需要在我的软件中收集一些统计数据,并且我正在努力使其快速且正确,这对我来说并不容易!

首先是我到目前为止的代码,有两个类,一个 StatsService 和一个 StatsHarvester

public class StatsService
{
private Map<String, Long>   stats   = new HashMap<String, Long>(1000);

public void notify ( String key )
{
    Long value = 1l;
    synchronized (stats)
    {
        if (stats.containsKey(key))
        {
            value = stats.get(key) + 1;
        }
        stats.put(key, value);
    }
}

public Map<String, Long> getStats ( )
{
    Map<String, Long> copy;
    synchronized (stats)
    {
        copy = new HashMap<String, Long>(stats);
        stats.clear();
    }
    return copy;
}
}

这是我的第二堂课,一个收集器,它不时收集统计数据并将其写入数据库。

public class StatsHarvester implements Runnable
{
private StatsService    statsService;
private Thread          t;

public void init ( )
{
    t = new Thread(this);
    t.start();
}

public synchronized void run ( )
{
    while (true)
    {
        try
        {
            wait(5 * 60 * 1000); // 5 minutes
            collectAndSave();
        }
        catch (InterruptedException e)
        {
            e.printStackTrace();
        }
    }
}

private void collectAndSave ( )
{
    Map<String, Long> stats = statsService.getStats();
    // do something like:
    // saveRecords(stats);
}
}

在运行时,它将有大约 30 个并发运行线程,每个线程调用notify(key)约100次。只有一个 StatsHarvester 正在呼叫statsService.getStats()

所以我有很多作家,但只有一个读者。拥有准确的统计数据固然很好,但我不在乎是否会在高并发情况下丢失一些记录。

读者应该每 5 分钟或任何合理的时间运行一次。

写作应该尽可能快。读取应该很快,但如果每5分钟锁定300ms左右,那就没问题了。

我读过很多文档(Java 并发实践、Effective Java 等),但我强烈感觉我需要您的建议才能正确执行。

我希望我的问题陈述得足够清楚和简短,以获得有价值的帮助。


EDIT

感谢大家的详细和有用的答案。正如我所期望的,有不止一种方法可以做到这一点。

我测试了你的大部分建议(我理解的)并将测试项目上传到谷歌代码以供进一步参考(maven项目)

http://code.google.com/p/javastats/ http://code.google.com/p/javastats/

我测试了 StatsService 的不同实现

  • 哈希映射统计服务(HMSS)
  • 并发HashMapStats服务(CHMSS)
  • 链接队列统计服务 (LQSS)
  • 谷歌统计服务 (GSS)
  • ExecutorConcurrentHashMapStatsService (ECHMSS)
  • ExecutorHashMapStatsService (EHMSS)

我测试了它们x每个调用notify的线程数y次,结果以毫秒为单位

         10,100   10,1000  10,5000  50,100   50,1000  50,5000  100,100  100,1000 100,5000 
GSS       1        5        17       7        21       117      7        37       254       Summe: 466
ECHMSS    1        6        21       5        32       132      8        54       249       Summe: 508
HMSS      1        8        45       8        52       233      11       103      449       Summe: 910
EHMSS     1        5        24       7        31       113      8        67       235       Summe: 491
CHMSS     1        2        9        3        11       40       7        26       72        Summe: 171
LQSS      0        3        11       3        16       56       6        27       144       Summe: 266

目前我想我会使用ConcurrentHashMap,因为它提供了良好的性能,而且很容易理解。

感谢您的输入! 詹宁


正如杰克所回避的那样,您可以使用 java.util.concurrent 库,其中包括 ConcurrentHashMap 和 AtomicLong。如果不存在,您可以将 AtomicLong 放入其中,否则您可以增加该值。由于 AtomicLong 是线程安全的,您将能够增加变量,而不必担心并发问题。

public void notify(String key) {
    AtomicLong value = stats.get(key);
    if (value == null) {
        value = stats.putIfAbsent(key, new AtomicLong(1));
    }
    if (value != null) {
        value.incrementAndGet();
    }
}

这应该既快速又线程安全

编辑:稍微重构,因此最多只有两次查找。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

java并发:多个写入器,一个读取器 的相关文章

随机推荐

  • 使用 Phonegap 应用程序执行 ajax 请求时出现问题

    我正在尝试使用 Phonegap 和 jQuery 创建一个简单的 RSS 阅读器 我正在关注这个教程 http visualrinse com 2008 09 24 how to build a simple rss reader wit
  • 来自 Android 的 HTTP 补丁请求

    我正在尝试从 Android 应用程序发出 HTTP 补丁请求 但无法弄清楚 HTTPUrlConnection 似乎不支持 PATCH HttpPatch 似乎不在 Android 包含的库中 link http hc apache or
  • Microsoft 团队获取来电号码

    我们有一个非常简单的用例 当团队中的用户被呼叫时 我们需要的只是呼叫者的号码 我们的想法是将这个数字传递到我们的后端系统并弹出相关信息 如果存在 所以只需要一种方法来读取呼叫者的号码 我查看了通话记录 API 但他们会提供已经发生的通话的信
  • MDX DRILLTHROUGH 失败但 select 可以成功执行

    我有以下 MDX 查询 它在执行时成功返回度量 SELECT Measures Closed Quote OE Retail ON COLUMNS FROM Sales WHERE Posting Date Date YQMD Month
  • sql服务器+VB 6.0

    我在 vb 6 0 中创建了一个数据库项目 我已经在 sql server 2000 中创建了数据库 我使用 adodc 和 odbc 连接到数据库 我想将我的 sql 服务器保留在一个系统中 将我的 vb 6 0 应用程序保留在另一个系统
  • 在 JSF 子视图中混合 HTML 和 JSF

    我今天遇到的问题是关于使用 JSF 处理包含的 JSP 中的 HTML 情况如下 我在 RAD 上使用 IBM 的 JSF 1 2 和 Websphere v6 1 我有一个自定义组件 来自公司层 来使用选项卡 为了获得更清晰的代码 我只想
  • Ember:动态切换到所选语言(使用 i18n 库)

    我正在使用 ember i18n 库来翻译我的应用程序中使用的静态字符串 由于语言文件相当大 我不想在应用程序启动时加载所有可能的语言词典 因此 我想在用户选择更改语言时动态加载字典 我已经做了第一个实现 效果相当好 See http js
  • 通过@leader@model访问automlleader时返回空列表

    Running h2o automl 返回排行榜中的单个模型 但是 当尝试通过访问实际模型时 leader model 出现以下错误 is H2OFrame x 中的错误 尝试从对象获取槽 指标 没有槽的基本类 NULL 另外 打电话时h2
  • 如何调试失败的 Fargate 任务初始化

    我有一个 Fargate 任务 已计划使用 CloudWatch Event 规则运行 并在成功运行时将时间戳输出到数据库 它还会在每次运行时向 CloudWatch 输出一个日志文件 但是 有 1 次未创建日志文件 并且未更新数据库 我怀
  • 为特定子域设置会话cookie

    我有一个具有多个子域的网站 它们共享一个唯一的 PHP 会话 cookie 来识别每个用户 我通过简单地添加来做到这一点session cookie domain mydomain com 但是我想知道是否可以指定多个子域 以便 cooki
  • Javascript 增量不起作用

    好吧 我不知道到底什么才是一个好的标题 因为这是一个最特殊的情况 或者我异常愚蠢 这就是我想做的 创建一个简单的
  • 数组元素的重复副本:MATLAB 中的游程解码

    我正在尝试使用 值 数组和 计数器 数组将多个值插入到数组中 例如 如果 a 1 3 2 5 b 2 2 1 3 我想要某个函数的输出 c somefunction a b to be c 1 1 3 3 2 5 5 5 其中 a 1 重复
  • 在 python/bash 脚本中编写 Git 挂钩 [关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 我最近需要编写 git hooks 以便所有提交都引用特定的票证 我希望有一个地方可以开始学习 Pro
  • C# 泛型接口协方差

    我有一个界面IControl
  • 自加入条件查询

    所以我有一个名为 用户 的表 其字段为 userId userName 主管 ID 我想获取特定用户的主管的用户名 CriteriaBuilder criteriaBuilder em getCriteriaBuilder Criteria
  • 如何使用 React Intl 动态添加语言环境数据?

    我正在使用 React intl 来实现 UI Util 库的国际化 该库有一个名为 i18n 的文件夹 其中我放置了不同区域设置的 json 文件 如果该库的用户想要添加对其他区域设置的支持 他 她可以放置带有相应区域设置的键 值对的附加
  • VS 2012 RC 中新增的 HTML 文档类型有哪些?

    我刚刚安装了 VS 2012 RC 在 HTML 工具栏下拉列表中它有很多新选项 文档类型 HTML5 HTML5 XHTML5 这两个 HTML5 选项有什么区别 我认为 HTML5 的建议是不要指定 DOCTYPE 所以第一个选项有点像
  • VBS 网站登录脚本 - “需要对象”错误

    我正在尝试编写我的第一个网站登录脚本 但总是在第 9 行位置 9 处收到错误消息 需要对象 getElementByID 800A01A8 这是我的实际工作站点的代码 Call Main Function Main Set IE WScri
  • ui-router 用于仅具有特定值的路由

    我正在尝试为具有以下结构的多个独特登陆页面构建一条路线 domain com state city category 如何定义路线 以便州 城市和类别只能是预定义值之一 又名 state ca ma ak az ar i city los
  • java并发:多个写入器,一个读取器

    我需要在我的软件中收集一些统计数据 并且我正在努力使其快速且正确 这对我来说并不容易 首先是我到目前为止的代码 有两个类 一个 StatsService 和一个 StatsHarvester public class StatsServic