原子指令:IF 和循环
Atomic Instructions: IFs and Loops
并发编程中的 IF 语句和循环(如 while 或 do while 原子指令)是原子指令吗?
如果没有,有没有办法自动实现它们?
编辑:修正了我的一些狡猾的英语。
这些可以有任意大且复杂(即非原子)的布尔表达式需要被评估。防止涉及它们的竞争条件的一种方法(如果那是你所说的 "appease" 的意思)是使用某种锁定机制。
在Java中,唯一不需要任何额外工作的原子就是赋值。其他任何东西都需要同步,通过声明方法 synchronized
或使用 synchronized
块。您还可以使用 java.concurrent
中的 类 - 其中一些使用一些更聪明的机制来确保同步,而不是仅仅声明方法 synchronized
往往很慢。
关于 if 语句和您在关于比较的评论中提出的问题 n == m
:
比较不是原子的。 n
的第一个值必须被加载(这里 m
的值仍然可以改变),然后 m
的值必须被加载,然后计算实际的比较(此时n
和 m
的实际值可能已经与比较中的不同。
如果你想让它同步,你必须这样做:
public class Test {
private static final Object lock = new Object();
public static void main(String[] args) {
if (equals(1, 2)) {
// do something (not synchronised)
}
}
public static boolean equals(int n, int m) {
synchronized (lock) {
return n == m;
}
}
}
然而,这提出了一个问题为什么要这样做以及锁应该是什么(以及锁与哪些线程共享)?我想了解有关您的问题的更多背景信息,因为目前我看不出有任何理由做这样的事情。
你还应该记住:
- 你不能锁定基元(你必须将两个值都声明为
Integer
)
- 锁定
null
将导致 NullPointerException
- 锁是在值上获取的,而不是在引用上。因为 Java 中的整数是不可变的,所以向字段分配新值将导致创建新锁,请参见下面的代码。线程
t1
获取 new Integer(1)
上的锁,而 t2
获取 new Integer(2)
上的锁。因此,即使两个线程都锁定 n
,它们仍然可以并行处理。
public class Test {
private static Integer n = 1;
public static void main(String[] args) throws Exception {
Thread t1 = new Thread(() -> {
synchronized (n) {
System.out.println("thread 1 started");
sleep(2000);
System.out.println("thread 1 finished");
}
});
Thread t2 = new Thread(() -> {
synchronized (n) {
System.out.println("thread 2 started");
sleep(2000);
System.out.println("thread 2 finished");
}
});
t1.start();
sleep(1000);
n = 2;
t2.start();
t1.join();
t2.join();
}
private static void sleep(int millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
您是否考虑过使用可变 AtomicInteger
?
并发编程中的 IF 语句和循环(如 while 或 do while 原子指令)是原子指令吗?
如果没有,有没有办法自动实现它们?
编辑:修正了我的一些狡猾的英语。
这些可以有任意大且复杂(即非原子)的布尔表达式需要被评估。防止涉及它们的竞争条件的一种方法(如果那是你所说的 "appease" 的意思)是使用某种锁定机制。
在Java中,唯一不需要任何额外工作的原子就是赋值。其他任何东西都需要同步,通过声明方法 synchronized
或使用 synchronized
块。您还可以使用 java.concurrent
中的 类 - 其中一些使用一些更聪明的机制来确保同步,而不是仅仅声明方法 synchronized
往往很慢。
关于 if 语句和您在关于比较的评论中提出的问题 n == m
:
比较不是原子的。 n
的第一个值必须被加载(这里 m
的值仍然可以改变),然后 m
的值必须被加载,然后计算实际的比较(此时n
和 m
的实际值可能已经与比较中的不同。
如果你想让它同步,你必须这样做:
public class Test {
private static final Object lock = new Object();
public static void main(String[] args) {
if (equals(1, 2)) {
// do something (not synchronised)
}
}
public static boolean equals(int n, int m) {
synchronized (lock) {
return n == m;
}
}
}
然而,这提出了一个问题为什么要这样做以及锁应该是什么(以及锁与哪些线程共享)?我想了解有关您的问题的更多背景信息,因为目前我看不出有任何理由做这样的事情。
你还应该记住:
- 你不能锁定基元(你必须将两个值都声明为
Integer
) - 锁定
null
将导致NullPointerException
- 锁是在值上获取的,而不是在引用上。因为 Java 中的整数是不可变的,所以向字段分配新值将导致创建新锁,请参见下面的代码。线程
t1
获取new Integer(1)
上的锁,而t2
获取new Integer(2)
上的锁。因此,即使两个线程都锁定n
,它们仍然可以并行处理。
public class Test {
private static Integer n = 1;
public static void main(String[] args) throws Exception {
Thread t1 = new Thread(() -> {
synchronized (n) {
System.out.println("thread 1 started");
sleep(2000);
System.out.println("thread 1 finished");
}
});
Thread t2 = new Thread(() -> {
synchronized (n) {
System.out.println("thread 2 started");
sleep(2000);
System.out.println("thread 2 finished");
}
});
t1.start();
sleep(1000);
n = 2;
t2.start();
t1.join();
t2.join();
}
private static void sleep(int millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
您是否考虑过使用可变 AtomicInteger
?