我需要在我的软件中收集一些统计数据,并且我正在努力使其快速且正确,这对我来说并不容易!
首先是我到目前为止的代码,有两个类,一个 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,因为它提供了良好的性能,而且很容易理解。
感谢您的输入!
詹宁