我需要在我的软件中收集一些统计信息,并且我试图使其快速正确,这对我来说并不容易!
到目前为止,我的代码首先包含两个类,即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的不同实现
我用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) 请帮忙。猪快把