如何使此 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 代码正常运行? [多线程,竞争条件]的主要内容,如果未能解决你的问题,请参考以下文章

java 如何编写多线程的代码

如何才能使此代码正常运行?我遇到了if和else语句的问题,显然它没有正确缩进

Java代码竞争条件多线程?

VB.net 如何设计多线程运行

java解决共享资源竞争

收藏一张图读懂 Java 多线程