Java 中的原子是保证顺序还是仅保证唯一性?
Do atomics in Java guarantee ordering or only uniqueness?
在执行以下代码片段时,我看到结果正在正确且唯一地递增,但是它们打印的顺序是乱序的(如下所示):
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
是否因为:
代码有问题(如果有,如何修复它以强制执行排序)?
这是原子的正确行为吗,因为在设计上它们不保证执行顺序而只保证无锁并发?
这里还有其他工作吗?
TIA。
为了解决我对下面评论的困惑 - 如果我仍然需要使用同步来查看下面发生的事情,为什么我首先需要 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;
}
}
}
}
};
无法保证您的代码如何交错。在您的情况下,我认为令人困惑的是循环不是作为一个单元执行的,而是可以以任何方式交错。想想线程 1 在执行 System.out.println(Thread.currentThread() + ": " + newValue)
之前停止,其中 newValue
从 0
递增到 1
所以还没有打印任何东西。然后线程 2 可以从 1
增加到 2
并在另一个线程之前打印它的输出。这将导致先打印较大的数字。
此外,请记住 System.out.println
在 System.out
上同步。这就是为什么您总是以正确的顺序观察大块印刷品的原因。一个线程可能发现 System.out
已锁定并在恢复其 activity.
之前暂停其 activity 一小会儿
在执行以下代码片段时,我看到结果正在正确且唯一地递增,但是它们打印的顺序是乱序的(如下所示):
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
是否因为:
代码有问题(如果有,如何修复它以强制执行排序)?
这是原子的正确行为吗,因为在设计上它们不保证执行顺序而只保证无锁并发?
这里还有其他工作吗?
TIA。
为了解决我对下面评论的困惑 - 如果我仍然需要使用同步来查看下面发生的事情,为什么我首先需要 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;
}
}
}
}
};
无法保证您的代码如何交错。在您的情况下,我认为令人困惑的是循环不是作为一个单元执行的,而是可以以任何方式交错。想想线程 1 在执行 System.out.println(Thread.currentThread() + ": " + newValue)
之前停止,其中 newValue
从 0
递增到 1
所以还没有打印任何东西。然后线程 2 可以从 1
增加到 2
并在另一个线程之前打印它的输出。这将导致先打印较大的数字。
此外,请记住 System.out.println
在 System.out
上同步。这就是为什么您总是以正确的顺序观察大块印刷品的原因。一个线程可能发现 System.out
已锁定并在恢复其 activity.