【问题标题】:Do atomics in Java guarantee ordering or only uniqueness?Java中的原子保证顺序还是唯一性?
【发布时间】:2016-09-09 19:26:19
【问题描述】:

执行以下 sn-p 时,我看到结果正在正确且唯一地递增,但是它们被无序打印(如下所示):

 import java.util.concurrent.atomic.AtomicInteger;

public class Atomics {

    private AtomicInteger index = new AtomicInteger(0);

    public static void main(String [] args) {
        new Atomics().updateIndexViaCAS();
    }

    private  void updateIndexViaCAS() {

        Runnable target = () -> {
            for(int i = 0; i<10;i++) {
                cas:
                while (true) {
                    int oldValue = index.get();
                    int newValue = oldValue + 1;
                    if(index.compareAndSet(oldValue, newValue)) {
                        System.out.println(Thread.currentThread() + ": "+newValue); //order is not guaranteed?
                        break cas;
                    }
                }
            }
        };

        /*Runnable target = () -> {
            for (int i = 0; i < 10; i++) {
                int oldValue = index.get();
                int newValue = oldValue + 1;
                do {
                    oldValue = index.get();
                } while (!index.compareAndSet(oldValue, newValue));
                System.out.println(Thread.currentThread() + ": "+newValue);
            }
        };*/

        for(int t=0; t<2;t++) {
            Thread th = new Thread(target);
            th.start();
        }


    }
}

示例结果:

Thread[Thread-0,5,main]: 1
Thread[Thread-0,5,main]: 3
Thread[Thread-0,5,main]: 4
Thread[Thread-0,5,main]: 5
Thread[Thread-0,5,main]: 6
Thread[Thread-1,5,main]: 2 <-- out of order
Thread[Thread-0,5,main]: 7
Thread[Thread-0,5,main]: 9
Thread[Thread-0,5,main]: 10
Thread[Thread-0,5,main]: 11
Thread[Thread-0,5,main]: 12
Thread[Thread-1,5,main]: 8  <-- out of order
Thread[Thread-1,5,main]: 13
Thread[Thread-1,5,main]: 14
Thread[Thread-1,5,main]: 15
Thread[Thread-1,5,main]: 16
Thread[Thread-1,5,main]: 17
Thread[Thread-1,5,main]: 18
Thread[Thread-1,5,main]: 19
Thread[Thread-1,5,main]: 20

是不是因为:

  1. 代码有问题(如果有,如何修复它以强制排序)?

  2. 这是不是原子的正确行为,因为在设计上它们不保证执行顺序,而只保证无锁并发?

  3. 这里还有其他工作吗?

TIA。

为了解决我对下面的 cmets 的困惑 - 如果我仍然需要使用同步来查看下面发生的情况,为什么我首先需要 Atomics?

Runnable target = () -> {
    for (int i = 0; i < 10; i++) {
        cas: while (true) {
            synchronized (mutex) {
                int oldValue = index.get();
                int newValue = oldValue + 1;
                if (index.compareAndSet(oldValue, newValue)) {
                    System.out.println(Thread.currentThread() + ": "
                            + newValue); // order is not guaranteed?
                    break cas;
                }
            }
        }
    }
};

【问题讨论】:

  • index 取值的顺序不一定是println 调用发生的顺序。compareAndSet 是原子的; compareAndSet-and-println 不是。
  • 它是#2。原子变量强制更新的原子性和更新的内存可见性。它们与订购无关。
  • @4castle: 那么,这是否意味着原子 CAS 真的对我没有任何好处,因为我仍然需要通过println 同步才能看到我感兴趣的内容?要不然是啥?代码示例表示赞赏。
  • 您必须同步包含compareAndSet()println() 的块。如果没有同步,另一个线程可能会在两者之间进行两次调用,因此会乱序打印。当然,如果你进行同步,你就不需要原子了。
  • 您可能对incrementAndGet函数感兴趣。

标签: java concurrency atomic java.util.concurrent java-memory-model


【解决方案1】:

无法保证您的代码如何交错。就您而言,我认为令人困惑的是循环不是作为一个单元执行的,而是可以以任何方式交错的。想想线程 1 在执行 System.out.println(Thread.currentThread() + ": " + newValue) 之前停止,其中 newValue0 递增到 1 所以还没有打印任何内容。然后线程 2 可以从 1 递增到 2 并在另一个线程之前打印其输出。这将导致首先打印更大的数字。

另外,请记住 System.out.printlnSystem.out 同步。这就是为什么你总是以正确的顺序观察大块的印刷品。一个线程可能发现System.out 被锁定,并在恢复其活动之前暂停其活动一小段时间。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-11-28
    • 1970-01-01
    • 2021-03-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多