java并发:许多作者,一个读者 [英] java concurrency: many writers, one reader

查看:108
本文介绍了java并发:许多作者,一个读者的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要在我的软件中收集一些统计数据,我试图使它快速和正确,这是不容易的(对我来说)。

I need to gather some statistics in my software and i am trying to make it fast and correct, which is not easy (for me!)

目前有两个类,StatsService和StatsHarvester

first my code so far with two classes, a StatsService and a 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;
}
}

这是我的第二个类,收集器

this is my second class, a harvester which collects the stats from time to time and writes them to a database.

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

At runtime it will have about 30 concurrent running threads each calling notify(key) about 100 times. Only one StatsHarvester is calling statsService.getStats()

所以我有很多作家,这将是很高兴有准确的统计,但我不在乎如果一些记录在高并发丢失。

So i have many writers and only one reader. it would be nice to have accurate stats but i don't care if some records are lost on high concurrency.

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

The reader should run every 5 Minutes or whatever is reasonable.

写作应尽可能快。阅读应该很快,但如果它锁定约300ms每5分钟,它的罚款。

Writing should be as fast as possible. Reading should be fast but if it locks for about 300ms every 5 minutes, its fine.

我已经阅读了许多文档(Java并发在实践中,有效的java等等),但我有强烈的感觉,我需要你的建议,得到它的权利。

I've read many docs (Java concurrency in practice, effective java and so on), but i have the strong feeling that i need your advice to get it right.

我希望我说明我的问题清楚且足够短,可以获得有价值的帮助。

I hope i stated my problem clear and short enough to get valuable help.

感谢您的详细和有用的答案。正如我预期的,有不止一种方法。

Thanks to all for your detailed and helpful answers. As i expected there is more than one way to do it.

我测试了大部分提案(我理解的),并将测试项目上传到Google代码以供进一步参考(maven project)

I tested most of your proposals (those i understood) and uploaded a test project to google code for further reference (maven project)

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

我测试了StatsService的不同实现

I have tested different implementations of my StatsService


  • HashMapStatsService(HMSS)

  • ConcurrentHashMapStatsService(CHMSS)

  • LinkedQueueStatsService(LQSS)

  • GoogleStatsService(GSS)

  • ExecutorConcurrentHashMapStatsService(ECHMSS)

  • ExecutorHashMapStatsService(EHMSS)

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

我用 x 测试它们每个调用通知的线程数 y 次,结果以ms计

and i tested them with x number of Threads each calling notify y times, results are in ms

         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,因为它提供了良好的性能,很容易理解。

At this moment i think i will use ConcurrentHashMap, as it offers good performance while it is quite easy to understand.

感谢您的所有投入!
Janning

Thanks for all your input! Janning

推荐答案

由于jack可以使用java.util.concurrent库,包括ConcurrentHashMap和原子龙。你可以把AtomicLong放在if中,如果没有,你可以增加这个值。由于AtomicLong是线程安全的,你将能够增加变量,而不必担心并发问题。

As jack was eluding to you can use the java.util.concurrent library which includes a ConcurrentHashMap and AtomicLong. You can put the AtomicLong in if absent else, you can increment the value. Since AtomicLong is thread safe you will be able to increment the variable without worry about a concurrency issue.

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();
    }
}

这应该是快速和线程安全的

This should be both fast and thread safe

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

Refactored sligthly so there is only at most two lookups.

这篇关于java并发:许多作者,一个读者的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆