【问题标题】:How do I make this Java code operate properly? [Multi-threaded, race condition]如何使此 Java 代码正常运行? [多线程,竞争条件]
【发布时间】: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 作为监视器

标签: java multithreading race-condition


【解决方案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 上使用同步块。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-10-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-10-08
      相关资源
      最近更新 更多