当前位置: 首页 > 面试题库 >

java并发性:很多作家,一个读者

汪欣德
2023-03-14
问题内容

我需要在我的软件中收集一些统计信息,并且我试图使其快速正确,这对我来说并不容易!

到目前为止,我的代码首先包含两个类,即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并发,有效的Java等),但是我有强烈的感觉,我需要您的建议才能正确解决问题。

我希望我说的问题清楚而简短,以期获得宝贵的帮助。

编辑

感谢所有您的详细和有用的答案。如我所料,有多种方法可以做到这一点。

我测试了您的大多数建议(我理解的那些建议),并将一个测试项目上传到Google代码以供进一步参考(Maven项目)

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

我已经测试了StatsService的不同实现

  • HashMapStatsService(HMSS)
  • ConcurrentHashMapStatsService(CHMSS)
  • LinkedQueueStatsService(LQSS)
  • GoogleStatsService(GSS)
  • ExecutorConcurrentHashMapStatsService(ECHMSS)
  • ExecutorHashMapStatsService(EHMSS)

我用x每个调用通知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,因为它提供了良好的性能,同时非常容易理解。

感谢您的输入!詹宁


问题答案:

正如jack所不及的,您可以使用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();
    }
}

这应该既快速又线程安全

编辑:重构,所以最多只有两个查找。



 类似资料:
  • Java 是最先支持多线程开发的语言之一, Java 多线程和并发也是 Java 学习的重点加难点。本教程根据作者多年 Java 开发经验总结而成,旨在帮助读者明白并发的原理。

  • 维基百科上说,下面的代码“增加了不允许任何线程饿死的限制”,我不明白为什么没有饿死。例如:如果有很多作者在任何读者之前到达,并且第一个作者花了很长时间完成他的写作,那么r可能会达到一些大的负数,比如说-12345,然后读者开始与作者一起到达,不知怎的,操作系统总是选择writer来接收信号量,而不是reader,如果那样的话,读者会挨饿,这是对的还是我错了?链接:读者和作者问题 请看链接中的第三个

  • 我有一个关于多使用者并发的问题。我想发送工作到rabbitmq来自web请求到分布式队列。我只想确定多队列中的工作顺序(FIFO)。因为此请求来自不同的用户,所以必须对eech用户请求/工作进行排序。 我已经在Azure ServiceBus和ActiveMQ消息分组上发现了这个具有不同名称的特性。 我想保证客户的要求必须相互订购。每个客户可能有多个请求,但必须按顺序处理针对该客户的请求。我希望在

  • 问题内容: 如何更改以下代码,以触发两个异步操作并有机会同时运行? 我需要做这样的事情吗? 问题答案: TL; DR 不要在获得承诺的问题中使用模式,而是分别等待它们;而是使用(至少现在): 虽然您的解决方案 确实 并行运行这两个操作,但如果两个诺言都被拒绝,它就无法正确处理拒绝。 细节: 您的解决方案并行运行它们,但始终等待第一个完成,然后再等待第二个。 如果您只想启动它们,并行运行它们,并获得

  • 问题内容: 我如何更改以下代码,以触发两个异步操作并有机会同时运行? 我需要做这样的事情吗? 问题答案: TL; DR 不要在获得承诺的问题中使用模式,而是分别等待它们;而是使用(至少现在): 虽然您的解决方案确实并行运行这两个操作,但是如果两个诺言都被拒绝,它就无法正确处理拒绝。 细节: 您的解决方案并行运行它们,但始终等待第一个完成,然后再等待第二个。如果您只想启动它们,并行运行它们,并获得两

  • 我有以下一袋数据: ({(key1,value1),(key1,value2)})({(key2,value1),(key2,value1)}) 上面的数据存储在一个文件/关系中&它有两行数据,每行都是一个包。 是否有一种方法来读取每个包,从包中发出每个元组? 例如:这里我想发出(key1,value1)(key1,value2)(key2,value1)(key2,value2) 请帮忙。猪快把