此代码中是否存在竞争条件?
Is there a race condition in this code?
我需要在 5 个线程中增加一个计数器,直到它达到 500。像这样的 5 个线程正在工作。但我需要知道它是否有竞争条件。
此代码的另一个问题是它以错误的顺序给我打印。
class HiloContador extends Thread {
static int count = 0;
@Override
public void run() {
for (int i = 0; i < 100; i++) {
synchronized (new Integer(count)) {
++count;
System.out.println(count);
}
}
}
}
输出
4
6
8
9
10
11
12
5
14
15
3
17
18
一些想法?
是的,存在竞争条件,因为您实际上是在不同对象上同步代码。
同步块代表一个临界区。要进入临界区,您必须获得全局锁。您可以使用对象进行锁定或使用整数。
这就是我要实现的方式:
class HiloContador extends Thread {
static int count = 0;
static Object lock = new Object();
@Override
public void run() {
for (int i = 0; i < 100; i++) {
synchronized (lock) {
++count;
System.out.println(count);
}
}
}
}
现在 println 将按预期顺序排列。
分析
Is there a race condition in this code?
是的,存在竞争条件:此行表示不正确的同步:
synchronized (new Integer(count)) {
原因如下。
让我们参考文档:
Every object has an intrinsic lock associated with it. By convention, a thread that needs exclusive and consistent access to an object's fields has to acquire the object's intrinsic lock before accessing them, and then release the intrinsic lock when it's done with them. A thread is said to own the intrinsic lock between the time it has acquired the lock and released the lock. As long as a thread owns an intrinsic lock, no other thread can acquire the same lock. The other thread will block when it attempts to acquire the lock.
— Intrinsic Locks and Synchronization (The Java™ Tutorials > Essential Classes > Concurrency).
在当前案例中,第一句话很关键。当前的实现使用新对象的内部锁在每次循环迭代时进行同步。这是不正确的。
解决方案概述
实现自增操作原子性的备选方案:
- 使用内部锁(
synchronized
关键字)。请参阅 Intrinsic Locks and Synchronization (The Java™ Tutorials > Essential Classes > Concurrency) 文章。
- 使用原子变量(
java.util.concurrent.atomic
包:AtomicBoolean
、AtomicInteger
、AtomicLong
等 classes)。请参阅 Atomic Variables (The Java™ Tutorials > Essential Classes > Concurrency) 文章。
- 使用锁对象(
java.util.concurrent.locks
包的 classes)。请参阅 Lock Objects (The Java™ Tutorials > Essential Classes > Concurrency) 文章。
最小解
让我们使用原子变量(解决方案 #3),即 AtomicInteger
class:它内置了所需的功能。
此外,我们不要扩展 Thread
class:让我们提取适当的 Runnable
接口实现。
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
public class Program {
public static void main(String[] args) throws InterruptedException {
final CountingRunnable countingRunnable = new CountingRunnable();
final List<Thread> threads = new ArrayList<>();
for (int i = 0; i < 5; ++i) {
final Thread thread = new Thread(countingRunnable);
threads.add(thread);
thread.start();
}
for (final Thread thread : threads) {
thread.join();
}
}
private static final class CountingRunnable implements Runnable {
private final AtomicInteger count = new AtomicInteger(0);
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(count.incrementAndGet());
}
}
}
}
其他参考文献
我需要在 5 个线程中增加一个计数器,直到它达到 500。像这样的 5 个线程正在工作。但我需要知道它是否有竞争条件。
此代码的另一个问题是它以错误的顺序给我打印。
class HiloContador extends Thread {
static int count = 0;
@Override
public void run() {
for (int i = 0; i < 100; i++) {
synchronized (new Integer(count)) {
++count;
System.out.println(count);
}
}
}
}
输出
4 6 8 9 10 11 12 5 14 15 3 17 18
一些想法?
是的,存在竞争条件,因为您实际上是在不同对象上同步代码。 同步块代表一个临界区。要进入临界区,您必须获得全局锁。您可以使用对象进行锁定或使用整数。 这就是我要实现的方式:
class HiloContador extends Thread {
static int count = 0;
static Object lock = new Object();
@Override
public void run() {
for (int i = 0; i < 100; i++) {
synchronized (lock) {
++count;
System.out.println(count);
}
}
}
}
现在 println 将按预期顺序排列。
分析
Is there a race condition in this code?
是的,存在竞争条件:此行表示不正确的同步:
synchronized (new Integer(count)) {
原因如下。
让我们参考文档:
Every object has an intrinsic lock associated with it. By convention, a thread that needs exclusive and consistent access to an object's fields has to acquire the object's intrinsic lock before accessing them, and then release the intrinsic lock when it's done with them. A thread is said to own the intrinsic lock between the time it has acquired the lock and released the lock. As long as a thread owns an intrinsic lock, no other thread can acquire the same lock. The other thread will block when it attempts to acquire the lock.
— Intrinsic Locks and Synchronization (The Java™ Tutorials > Essential Classes > Concurrency).
在当前案例中,第一句话很关键。当前的实现使用新对象的内部锁在每次循环迭代时进行同步。这是不正确的。
解决方案概述
实现自增操作原子性的备选方案:
- 使用内部锁(
synchronized
关键字)。请参阅 Intrinsic Locks and Synchronization (The Java™ Tutorials > Essential Classes > Concurrency) 文章。 - 使用原子变量(
java.util.concurrent.atomic
包:AtomicBoolean
、AtomicInteger
、AtomicLong
等 classes)。请参阅 Atomic Variables (The Java™ Tutorials > Essential Classes > Concurrency) 文章。 - 使用锁对象(
java.util.concurrent.locks
包的 classes)。请参阅 Lock Objects (The Java™ Tutorials > Essential Classes > Concurrency) 文章。
最小解
让我们使用原子变量(解决方案 #3),即 AtomicInteger
class:它内置了所需的功能。
此外,我们不要扩展 Thread
class:让我们提取适当的 Runnable
接口实现。
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
public class Program {
public static void main(String[] args) throws InterruptedException {
final CountingRunnable countingRunnable = new CountingRunnable();
final List<Thread> threads = new ArrayList<>();
for (int i = 0; i < 5; ++i) {
final Thread thread = new Thread(countingRunnable);
threads.add(thread);
thread.start();
}
for (final Thread thread : threads) {
thread.join();
}
}
private static final class CountingRunnable implements Runnable {
private final AtomicInteger count = new AtomicInteger(0);
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(count.incrementAndGet());
}
}
}
}