在Java中编写一个线程安全的模块化计数器

完全免责声明:这不是一个功课,但我标记为这样,因为它主要是一个自学习的练习,而不是实际上是“工作”。

假设我想在Java中编写一个简单的线程安全模块计数器。也就是说,如果模M为3,则计数器应该循环遍历0,1,2,0,1,2,…无限。

这是一个尝试:

import java.util.concurrent.atomic.AtomicInteger;

public class AtomicModularCounter {
    private final AtomicInteger tick = new AtomicInteger();
    private final int M;

    public AtomicModularCounter(int M) {
        this.M = M;
    }
    public int next() {
        return modulo(tick.getAndIncrement(), M);
    }
    private final static int modulo(int v, int M) {
        return ((v % M) + M) % M;
    }
}

我的这个代码的分析(可能是错误的)是因为它使用了AtomicInteger,即使没有任何明确的同步方法/块,它也是线程安全的。

不幸的是,“算法”本身并不是很“工作”,因为当在Integer.MAX_VALUE周围打勾时,next()可能返回错误的值,取决于模M。也就是说:

System.out.println(Integer.MAX_VALUE + 1 == Integer.MIN_VALUE); // true
System.out.println(modulo(Integer.MAX_VALUE, 3)); // 1
System.out.println(modulo(Integer.MIN_VALUE, 3)); // 1

也就是说,当模数为3并且包围时,对next()的两个调用将返回1,1。

next()可能会出现乱序问题,例如:

> Thread1 call next()
> Thread2调用next()
Thread2完成了tick.getAndIncrement(),返回x
> Thread1完成tick.getAndIncrement(),返回y = x 1(mod M)

在这里,除了前面提到的包装问题,x和y确实是为这两个next()调用返回的两个正确的值,但是根据如何指定counter行为,可以认为它们是乱序的。也就是说,我们现在有(Thread1,y)和(Thread2,x),但也可以指定(Thread1,x)和(Thread2,y)是“正确”的行为。

所以通过某些词的定义,AtomicModularCounter是线程安全的,但实际上并不是原子的。

所以问题是:

我的分析是否正确?如果没有,请指出任何错误。
>以上我使用正确的术语上面的陈述?如果没有,什么是正确的声明?
>如果上述问题是真实的,那么你将如何解决?
>你可以通过利用AtomicInteger的原子来修复它而不使用synchronized吗?
>你如何写它,使得tick本身是由模数范围控制,甚至没有机会包装Integer.MAX_VALUE?

>如果需要,我们可以假设M至少是一个小于Integer.MAX_VALUE的顺序

附录

以下是无序“问题”的列表类比。

> Thread1调用add(first)
> Thread2调用add(second)

现在,如果我们将列表更新成功添加了两个元素,但是第二个来到第一个,最后是“线程安全”?

如果这是“线程安全”,那不是什么呢?也就是说,如果我们指出在上面的情况下,首先应该来到第二个,这个并发属性叫什么? (我称之为“原子性”,但我不知道这是否是正确的术语)。

对于什么是值得的,关于这个乱序方面的Collections.synchronizedList行为是什么?

只要我看到你只需要一个变体的getAndIncrement()方法

public final int getAndIncrement(int modulo) {
    for (;;) {
        int current = atomicInteger.get();
        int next = (current + 1) % modulo;
        if (atomicInteger.compareAndSet(current, next))
            return current;
    }
}
http://stackoverflow.com/questions/3429787/writing-a-thread-safe-modular-counter-in-java

本站文章除注明转载外,均为本站原创或编译
转载请明显位置注明出处:在Java中编写一个线程安全的模块化计数器