【问题标题】:Two threads miss value in loop [closed]两个线程在循环中缺少值[关闭]
【发布时间】:2013-01-01 23:48:03
【问题描述】:

我有这样的代码:

public class Count extends Thread {
    static IntCell n = new IntCell();

    public void run() {
        int temp;
        for (int i = 0; i < 200000; i++) {
            temp = n.getN();
            n.setN(temp + 1);
        }
    }

    public static void main(String[] args) {
        Count p = new Count();
        p.setName("Watek1");
        Count q = new Count();

        p.start();
        q.start();

        try {
            p.join();
            q.join();  
        }
        catch (InterruptedException e) {
            System.out.println(e);
        }

        System.out.println("The value of n is " + n.getN());
    }
}

class IntCell {
    private int n = 0;
    public int getN() {
        return n;
    }
    public void setN(int n) {
        this.n = n;
    }
}

有两个线程,它们将 n 的值加 1(在静态类中)。当我运行此代码时,n 值的值永远不会等于 400000,而是与此有关。 为什么会发生这样的事情?

【问题讨论】:

  • 此代码看起来像是您故意造成了竞争条件。这是一个教科书的例子。你为什么要写这个然后问为什么会这样?
  • 因为您没有互斥锁(或等效机制)来防止竞争条件。
  • @Matti Virkkunen 我闻到了作业的味道 :-)
  • @sarcan 是的,这是家庭作业;)。我花了很多时间,我仍然不明白,但现在一切都清楚了:)。

标签: java multithreading class static


【解决方案1】:

这是多线程的基本问题之一。获取一个值、递增它并将其存储回来并不是所谓的“原子”——也就是说,需要多个操作才能做到这一点。这里发生的情况是:一个线程在值 X 处获得 N,另一个线程也这样做。两个线程都递增,并将 X+1 存储为新的 N - 虽然它们的本地计数都递增了 1,但 N 也只是递增了 1。

无论是否发生这种情况以及 N 会偏离预期值多少,出于争论的原因,都是随机的。

查看 AtomicInteger 之类的类并阅读称为互斥锁的东西。

【讨论】:

  • 可以说两个线程同时运行,N的值没有时间改变?例如:N = 100; Thread1 运行并将 N 更改为 101,Thread2 运行并获得 N = 100 的值并更改为 101?
  • @SzymonBarylak 是的,也不是。结果是这样,但并不是“N的值没有时间改变”。我们可以简单地说,一个警察,即 JVM 调度程序,在它的工作中间强行停止一个线程,让另一个线程让路。第一个线程没有时间完成它正在做的事情。使用同步或其他技巧告诉警察在第一次完成工作之前不要授予对其他线程的访问权限。希望我的比喻有所帮助
【解决方案2】:

出现竞争条件。见同步语句(下)...

public void run() {
    int temp;
    for (int i = 0; i < 200000; i++) {
        synchronized(n) {
          temp = n.getN();
          n.setN(temp + 1);
        }
    }
}

【讨论】:

    【解决方案3】:

    对您的问题的简单回答是因为两个线程执行的 n.getN()n.setN(temp + 1) 的调用相互交错,因为示例:

    Thread 1: n.getN() // returns 100
    Thread 2: n.getN() // also returns 100
    Thread 1: n.setN(temp + 1) // sets 101
    Thread 2: n.setN(temp + 1) // also sets 101
    

    您需要实现线程同步或其他方法才能达到预期的效果。

    我建议您花一些时间阅读 Java SE 教程的并发章节:http://docs.oracle.com/javase/tutorial/essential/concurrency/index.html

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-07-20
      • 2014-08-03
      • 1970-01-01
      • 2020-02-22
      • 1970-01-01
      • 1970-01-01
      • 2012-01-03
      • 1970-01-01
      相关资源
      最近更新 更多