原子循环计数器

感谢同事[孙棋]的投稿

现实当中很多场景,需要进行轮训服务,比如轮训在10个日志文件当中写日志,在10台机器上轮训的去调用以实现负载均衡,常规的做法,如tomcat的Poller线程轮训选择,就采用

Math.abs(pollerRotater.incrementAndGet()) % pollers.length

此地需要取原子自增的绝对值模以poller线程数,那是否有更好的实现呢?

public class CycleAtomicInteger {
private final static long PARK_TIME = 1000L * 1000;

private AtomicInteger counter = new AtomicInteger(0);

private int range;

public CycleAtomicInteger(int range) {
    if (range < 2)
        throw new IllegalArgumentException();
    this.range = range;
}

/**
 * 获取下个原子值
 *
 * @return
 */
public int next() {
    for (;;) {
        int c = counter.get();
        int next = (c + 1) % range;
        if (counter.compareAndSet(c, next)) {
            return c;
        } else {
            LockSupport.parkNanos(PARK_TIME);
        }
    }
}

}

这样就可以快速的实现rr的效果,同时也避免了abs的过程,至于LockSupport.parkNanos(PARK_TIME);加了这个后,4个线程执行2亿次的计算,我本机从原来的16s减少到4s,至于为什么要加这个,可参见更快的AtomicInteger

当然,这样设计会存在cas的aba问题,但对当前的case需求,其实是满足的,也不存在问题

原创文章,转载请注明: 转载自并发编程网 – ifeve.com本文链接地址: 原子循环计数器



sunqi

花名空蒙,阿里-无线事业部-资深开发工程师。目前负责MTOP的设计和开发工作。

Latest posts by sunqi (see all)

FavoriteLoading添加本文到我的收藏
  • 5,080 人阅读
  • 8 comments
  • Trackback are closed
  • Comments (8)
    • eric
    • 07/26. 2014 10:16pm

    The link is inaccessible

    • teasp
    • 07/30. 2014 1:36pm

    “这样设计会存在cas的aba问题,但对当前的case需求,其实是满足的,也不存在问题 ”

    请问为什么不存在问题?我看着怎么觉得存在aba的问题呢?

    • teasp
    • 07/30. 2014 1:38pm

    我明白了,你是说在现实场景中aba不造成问题。

  1. 这里有两个问题:
    1. park的时间,现在是1ms ,这个时间的长短还是需要斟酌的,lz有没有试过调整这个值看效果?
    2. lz的这个方案,只是多了一个park的过程,但和开始提出的问题比优点在哪呢? 也用到了 cas 也用到了incrementAndGet,区别究竟在哪?或者说,他为什么效率更高?能解释下么?

      • sunqi
      • 07/31. 2014 3:55pm

      1、减少了一次Math.abs
      2、park的时间长短,确实是应该依据具体情况来,就我本地机器与服务器的测试,1ms相对是合适的,具体见http://ifeve.com/better_atomicinteger/
      主要提升的原因,是在激烈的竞争下,避免了cas的自旋

    • 康杜
    • 08/07. 2014 10:47am

    应该是轮询不是轮训

    • hailiang
    • 10/29. 2014 11:26am

    应该是1ns不是1ms

You must be logged in to post a comment.

return top