【问题标题】:Parallel Programming 2 Threads Running With Shared Variable并行编程 2 个使用共享变量运行的线程
【发布时间】:2016-07-13 09:21:00
【问题描述】:

我有一个关于并发的问题,我刚刚编写了一个运行 2 个线程的程序,说明如下:

Thread 1: increment by 1 the variable "num" till 1'000'000 with loop
Thread 2: same thing but decrementing

最后我收到了不想要的结果。是的,我知道我可以同步或尝试使用可重入锁,但问题是我无法理解所有这些不同的不良结果背后的原因。

我的意思是我使用的操作是可交换的,因此我们不关心排序,所以如果这无关紧要,我们仍然应该获得 0,但事实并非如此!

有人可以向我解释一下所有计算背后发生了什么,这样我就可以感受一下并立即识别这种情况?

编辑: 因为我只是对理解主要概念感兴趣,所以我认为没有必要放代码。

代码:

class MyThread implements Runnable {
int id;
volatile static long num = 0;


MyThread(int id) {
    this.id = id;

public void run() {
    if (id == 0) {
            for (int j = 0; j < 100000; ++j)
                num++;}
    } else {
            for (int j = 0; j < 100000; ++j)
                num--;}

之后我创建线程并运行它们:

    MyThread p = new MyThread(0);
    MyThread q = new MyThread(1);
    Thread t = new Thread(p);
    Thread u = new Thread(q);
    t.start();
    u.start();
    try {
        t.join();
        u.join();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

编辑2: 我现在理解了这个概念,但我也想知道为什么将变量声明为 volatile 仍然会给我错误的结果?

EDIT3:我考虑过,我认为这是因为糟糕的交错仍然会产生问题!

【问题讨论】:

  • 代码在哪里?
  • "an undesired result" 您的问题中没有给出您想要或不想要的结果,因此很难理解您的问题。
  • 这个问题已经回答了很多次了,但是我建议你如果能自己解决的话会学到更多。答案并不复杂。提示:分解--++ 真正要做的事情。
  • @AndyTurner 期望的结果写成 (0),这意味着不期望的结果与 0 不同(每次运行后它给出不同的结果)。但是我现在还是附上了代码!
  • @AndyTurner 虽然没有指定,但这个问题已经被问了很多次,假设它是同一个问题是很安全的。 ;)

标签: java multithreading concurrency parallel-processing


【解决方案1】:

如果递增/递减操作不是原子的,则可能会出现这种行为。
一个操作被认为是原子的如果它在系统的其余部分看来是瞬间发生的。 (参见wikipedia)。

考虑以下情况:

  1. Thread 1 读取变量x 中的值n
  2. Thread 2 读取变量x 中的值n
  3. Thread 1 递增该值并将其存储在变量 x 中,该变量现在计算为 n+1
  4. Thread 2 递减该值并将其存储在变量x 中,该变量现在评估为n-1

但您想要的是变量 x 仍然在 n 进行评估。

我不知道 java 原语的具体情况,但您似乎可以使用 AtomicInteger 或使用 synchronized 方法在这里解决您的问题。

【讨论】:

  • javamex.com/tutorials/synchronization_volatile.shtml 可以看出,volatile 只是确保读写是线程安全的,但是:“因为访问 volatile 变量永远不会持有锁,所以它不适合我们想要读取-更新-写入作为原子操作(除非我们准备“错过更新”);“。因此,您必须使用一些更复杂的类来确保增量/减量是原子的,而 volatile 则不是。更简单的可能是 java.util.concurrent.atomic 包中的 AtomicInteger。
  • 感谢您的回答
【解决方案2】:

只需将此字段标记为volatile。通过这种方式,您将获得安全访问,并且您将能够在多线程应用程序中更改它,而无需使用任何其他同步工具。

【讨论】:

  • 这将使它不那么糟糕,但不会阻止“不希望的”结果。
  • 哦,在我读到的每一篇关于并发的文章中,作者都​​非常注意结果。他们说类似“程序的输出可以如下”。所以他们不确定。
  • 你永远不知道并发线程有多快,哪个更快的原因。如果你想达到它,你必须使用锁或其他同步工具,如你所说。
  • @antongarakh 不,原因是int 的输入/递减不是原子的,并且线程交错。
  • @antongarakh AtomicLong 使用比较和设置以及原子添加操作。
猜你喜欢
  • 1970-01-01
  • 2016-02-09
  • 2013-11-12
  • 1970-01-01
  • 1970-01-01
  • 2018-08-28
  • 1970-01-01
  • 1970-01-01
  • 2021-08-14
相关资源
最近更新 更多