如何使此 Java 代码正常运行? [多线程,竞争条件]
Posted
技术标签:
【中文标题】如何使此 Java 代码正常运行? [多线程,竞争条件]【英文标题】:How do I make this Java code operate properly? [Multi-threaded, race condition] 【发布时间】:2012-12-13 01:15:01 【问题描述】:我从一个学生那里得到了这个代码,由于涉及x++
和x--
的竞争条件,它不能正常工作。他在run()
方法中添加了synchronized
以试图摆脱这个错误,但显然这只排除了线程在same 对象上输入run()
(这在第一个从来不是问题place) 但不会阻止独立对象同时更新同一个静态变量x
。
public class DataRace implements Runnable
static volatile int x;
public synchronized void run()
for (int i = 0; i < 10000; i++)
x++;
x--;
public static void main(String[] args) throws Exception
Thread [] threads = new Thread[100];
for (int i = 0; i < threads.length; i++)
threads[i] = new Thread(new DataRace());
for (int i = 0; i < threads.length; i++)
threads[i].start();
for (int i = 0; i < threads.length; i++)
threads[i].join();
System.out.println(x); // x not always 0!
由于我们无法在x
上同步(因为它是原始的),我能想到的最佳解决方案是创建一个新的静态对象,如static String lock = "";
,并将x++
和x--
包含在@987654332 中@块,锁定lock
。但这似乎真的很尴尬。有没有更好的办法?
【问题讨论】:
您需要同时或分别同步x++
和x--
这两个操作吗?顺便说一句,使用锁来解决这个问题似乎并不尴尬(至少对我来说不是)。
用AtomicInteger
替换int
或同步DataRace.class
..或者你可以使用“类对象”。即使用 DataRace.class 作为监视器
【参考方案1】:
使用AtomicInteger
做你想做的事,它明确表示对x
的操作是原子的。经过多次运行以下操作,我每次都得到0
:
import java.util.concurrent.atomic.AtomicInteger;
public class DataRace implements Runnable
static volatile AtomicInteger x = new AtomicInteger(0);
public void run()
for (int i = 0; i < 10000; i++)
x.incrementAndGet();
x.decrementAndGet();
public static void main(String[] args) throws Exception
Thread[] threads = new Thread[100];
for (int i = 0; i < threads.length; i++)
threads[i] = new Thread(new DataRace());
for (int i = 0; i < threads.length; i++)
threads[i].start();
for (int i = 0; i < threads.length; i++)
threads[i].join();
System.out.println(x); // x **is now** always 0!
【讨论】:
你甚至不需要synchronized
,因为直到最后你才看x
。如果没有synchronized
,您的意思是 JVM 可以随意散布方法调用——可以连续两次递增,然后递减两次。但这很好,因为只要增量的总数与减量的总数相同,并且只要每个增量/减量都是原子的(AtomicInteger
保证),最终总和仍将为 0。如果你想在每个循环结束时检查 0,然后你会 synchronized
在一些静态锁上。
@yshavit,你说得对,我会相应地更新我的答案。
@yshavit 他不需要synchronized
,因为它只阻止了两个线程在同一个对象上执行(正如我在原始帖子中提到的)从未发生过: 100 个创建的对象中的每一个都由其自己的线程操作。你是对的,如果我们想在每个循环结束时打印x
,我们需要另一个同步机制来确保 inc 和 dec 始终以原子对运行。【参考方案2】:
AtomicInteger 就是你要找的东西。
【讨论】:
【参考方案3】:变量x
是静态的并且驻留在一个类中,因此对它的访问应该在该类上同步:要么创建一个静态方法,要么在DataRace.class
上使用同步块。
【讨论】:
以上是关于如何使此 Java 代码正常运行? [多线程,竞争条件]的主要内容,如果未能解决你的问题,请参考以下文章