java - 这个线程安全的字节序列生成器有什么问题?

标签 java concurrency puzzle atomic

我需要一个字节生成器来生成从 Byte.MIN_VALUE 到 Byte.MAX_VALUE 的值。当它达到 MAX_VALUE 时,应该从 MIN_VALUE 重新开始。

我已经使用 AtomicInteger 编写了代码(见下文);但是,如果并发访问并且使用 Thread.sleep() 人为地减慢代码(如果没有 hibernate ,它运行良好;但是,我怀疑它对于并发问题的出现来说太快了),代码似乎不会正常运行。

代码(添加了一些调试代码):

public class ByteGenerator {

    private static final int INITIAL_VALUE = Byte.MIN_VALUE-1;

    private AtomicInteger counter = new AtomicInteger(INITIAL_VALUE);
    private AtomicInteger resetCounter = new AtomicInteger(0);

    private boolean isSlow = false;
    private long startTime;

    public byte nextValue() {
        int next = counter.incrementAndGet();
        //if (isSlow) slowDown(5);
        if (next > Byte.MAX_VALUE) {
            synchronized(counter) {
                int i = counter.get();
                //if value is still larger than max byte value, we reset it
                if (i > Byte.MAX_VALUE) {
                    counter.set(INITIAL_VALUE);
                    resetCounter.incrementAndGet();
                    if (isSlow) slowDownAndLog(10, "resetting");
                } else {
                    if (isSlow) slowDownAndLog(1, "missed");
                }
                next = counter.incrementAndGet();
            }
        }
        return (byte) next;
    }

    private void slowDown(long millis) {
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
        }
    }
    private void slowDownAndLog(long millis, String msg) {
        slowDown(millis);
        System.out.println(resetCounter + " " 
                           + (System.currentTimeMillis()-startTime) + " "
                           + Thread.currentThread().getName() + ": " + msg);
    }

    public void setSlow(boolean isSlow) {
        this.isSlow = isSlow;
    }
    public void setStartTime(long startTime) {
        this.startTime = startTime;
    }

}

然后,测试:

public class ByteGeneratorTest {

    @Test
    public void testGenerate() throws Exception {
        ByteGenerator g = new ByteGenerator();
        for (int n = 0; n < 10; n++) {
            for (int i = Byte.MIN_VALUE; i <= Byte.MAX_VALUE; i++) {
                assertEquals(i, g.nextValue());
            }
        }
    }

    @Test
    public void testGenerateMultiThreaded() throws Exception {
        final ByteGenerator g = new ByteGenerator();
        g.setSlow(true);
        final AtomicInteger[] counters = new AtomicInteger[Byte.MAX_VALUE-Byte.MIN_VALUE+1];
        for (int i = 0; i < counters.length; i++) {
            counters[i] = new AtomicInteger(0);
        }
        Thread[] threads = new Thread[100];
        final CountDownLatch latch = new CountDownLatch(threads.length);
        for (int i = 0; i < threads.length; i++) {
            threads[i] = new Thread(new Runnable() {
                public void run() {
                    try {
                        for (int i = Byte.MIN_VALUE; i <= Byte.MAX_VALUE; i++) {
                            byte value = g.nextValue();
                            counters[value-Byte.MIN_VALUE].incrementAndGet();
                        }
                    } finally {
                        latch.countDown();
                    }
                }
            }, "generator-client-" + i);
            threads[i].setDaemon(true);
        }
        g.setStartTime(System.currentTimeMillis());
        for (int i = 0; i < threads.length; i++) {
            threads[i].start();
        }
        latch.await();
        for (int i = 0; i < counters.length; i++) {
            System.out.println("value #" + (i+Byte.MIN_VALUE) + ": " + counters[i].get());
        }
        //print out the number of hits for each value
        for (int i = 0; i < counters.length; i++) {
            assertEquals("value #" + (i+Byte.MIN_VALUE), threads.length, counters[i].get());
        }
    }

}

我的 2 核机器上的结果是值 #-128 获得 146 次命中(因为我们有 100 个线程,所有这些都应该平均获得 100 次命中)。

如果有人有任何想法,这段代码有什么问题,我洗耳恭听。

更新:对于那些赶时间又不想向下滚动的人来说,用 Java 解决这个问题的正确(也是最短和最优雅)的方法是这样的:

public byte nextValue() {
   return (byte) counter.incrementAndGet();
}

谢谢,亨氏!

最佳答案

最初,Java 将所有字段存储为 4 或 8 字节的值,甚至短整型和字节。字段上的操作将简单地进行位掩码以缩小字节。因此我们可以很容易地做到这一点:

public byte nextValue() {
   return (byte) counter.incrementAndGet();
}

有趣的小谜题,谢谢 Neeme :-)

关于java - 这个线程安全的字节序列生成器有什么问题?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7384875/

相关文章:

java - 为什么链表克隆方法实现需要将复制的列表存储为原始状态?

c - C将如何解析这句话?

java - Eclipse 没有将我的项目识别为 Java

java - 处理(Java可视化语言): spinning rectangle leaving odd fading trail effect with rectMode(CENTER) but not rectMode(CORNER)

c++ - 在此代码中使用 memory_order_relaxed 是否正确?

.net - Scala.net 中的 Actor

java - javafixedThreadPool可以被多个线程使用吗

sql - 有趣的 SQL 谜题

c# - 计算两个计时器值之间的平均值

java - JUnit、Mockito 和 Powermock 访问 protected 本地方法