java并发:许多作者,一个读者 [英] java concurrency: many writers, one reader
问题描述
我需要在我的软件中收集一些统计数据,我试图使它快速和正确,这是不容易的(对我来说)。
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屋!