我遇到了以下问题: 我已经实现了一个爬虫,我想知道在最后一秒内完成了多少请求,以及在最后一秒内下载了多少数据。
目前,我已经使用锁实现了它。我的版本使用一个队列和两个计数器(count和sum)。 任务完成后,我只需增加计数器,然后将一个事件(包含当前日期)添加到队列中 当想要获取我的计数器的值时,我检查队列中的某些东西是否超过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