如何实现原子请求计数器

时间:2013-11-01 17:51:55

标签: java concurrency locking queue

我遇到了以下问题: 我已经实现了一个爬虫,我想知道在最后一秒内完成了多少请求,以及在最后一秒内下载了多少数据。

目前,我已经使用锁实现了它。我的版本使用一个队列和两个计数器(count和sum)。 任务完成后,我只需增加计数器,然后将一个事件(包含当前日期)添加到队列中 当想要获取我的计数器的值时,我检查队列中的某些东西是否超过1秒。如果是这样,我将它出列并正确减少我的计数器。然后,我返回想要的结果。

这个版本运行良好,但出于培训目的,我希望使用原子操作而不是锁来重新实现它。不过,我承认我坚持“清洁行动”。 (旧价值的出列)

那么,这是实现这个目标的好方法吗?

我可以使用哪种其他方法?

谢谢!

1 个答案:

答案 0 :(得分:1)

  

这个版本运行良好,但出于培训目的,我希望使用原子操作而不是锁来重新实现它。

如果您需要在滚动周期发生时对数据进行多次更改,则需要锁定,否则会出现问题。每当你有多个“原子操作”时,你需要锁定以防止竞争条件。例如,在您的情况下,如果在您进行滚动时将其他内容添加到队列中该怎么办?

  

我可以使用哪种其他方法?

我不是100%肯定你为什么需要排队信息。如果您只计算请求数量和下载的数据大小总数,那么您应该能够使用单个AtomicReference<CountSum>CountSum类会存储您的两个值。然后,当有人需要增加它时,他们会做类似的事情:

CountSum newVal = new CountSum();
do {
   CountSum old = countSumRef.get();
   newVal.setCount(old.getCount() + 1);
   newVal.setSum(old.getSum() + requestDataSize);
   // we need to loop here if someone changed the value behind our back
} while (!countSumRef.compareAndSet(old, newVal));

这可确保您的计数和总和始终保持同步。如果您使用了两个AtomicLong变量,则必须进行两次原子请求并再次需要锁定。

当你想重置这些值时,你会做同样的事情。

CountSum newVal = new CountSum(0, 0);
CountSum old;
do {
   old = countSumRef.get();
   // we need to loop here if someone changed the value behind our back
} while (!countSumRef.compareAndSet(old, newVal));
// now you can display the old value and be sure you got everything