2

该页面的作者提到,下面的第二个代码示例存在同步问题,但该代码在 100 次中可以运行大约 99 次。我无法看到该页面中两个程序的操作差异。


结合前两者的更通用的解决方案是将字段的值复制到局部变量中,然后仅更改局部变量。该字段在方法内保持不变。例如,

public class Counter {

  int count = 0;

  public void count() {
    int count = this.count;
    int limit = count + 100;
    while (count++ != limit) System.out.println(count); 
  }

}

请注意局部变量 count 如何隐藏字段计数,以及 this 关键字如何用于引用阴影之外的字段计数。

当方法完成后不需要将更改的变量保存回字段时,此技巧主要有用。以下保存了状态,但仍然存在不太明显的同步问题。

public class Counter {

  private int count = 0;

  public void count() {
    int count = this.count;
    int limit = count + 100;
    while (count++ != limit) System.out.println(count);
    this.count = count; 
  }

}

事实上,这可能比原来的例子更糟糕,因为它会在 100 次中工作 99 次。如果你没有在源代码中发现它,这里的错误很难确定。

4

1 回答 1

2

问题在于修改 tothis.count不是原子的。

假设有两个线程正在调用此函数,并且this.count当前设置为0.

线程 1 加载0到其本地count,然后开始将其递增到101(不像100以前认为的那样,因为它count在达到限制后再递增一次,使用count++ != limit)。

与此同时,线程 2 进入并仍然this.count设置为,因此它抓住它并开始递增到.0101

在某个时刻,它们都到达了这条线this.count = count(顺序无关紧要),它们设置this.count为 101。

最后应该设置的是202(我认为 - 当然它应该在某个地方的200标记附近)。

代码的第一位有效的原因是因为它实际上并没有尝试更改this.count值,因此不可能出现竞争条件。

于 2013-02-12T00:30:21.023 回答