为什么一根线会留下线条



我是编程新手,研究线程已经有一段时间了。

因此,以下代码应该给出的输出为:

one 98098
two 98099

有时确实如此。

当我尝试运行它几次时,它会给出不同的输出。我可以理解JVM控制线程,我不能直接影响它,但有些输出小于98000,尽管for循环98次增加了1000。这是怎么发生的?一根线能留下线条吗?或者我做错了什么(注意:预期的输出有时会显示在屏幕上,但并不总是)

public class TestThreads {
public static void main(String [] args) {
ThreadOne t1 = new ThreadOne();
Thread one = new Thread(t1);
ThreadTwo t2 = new ThreadTwo();
Thread two = new Thread(t2);
one.start();    
two.start();
}
}
class Accum {
private int counter = 0;
private static Accum a = new Accum();
private Accum() {
}
public static Accum getAccum() {
return a;
}
public int getCount() {
return counter;
}
public void updateCounter(int add) {
counter += add;
}
}
class ThreadOne implements Runnable {
Accum a = Accum.getAccum();
public void run() {
for(int x=0; x < 98; x++) {
a.updateCounter(1000);
try {
Thread.sleep(50);
} catch(InterruptedException ex) { }
}
System.out.println("one "+a.getCount());
}
}
class ThreadTwo implements Runnable {
Accum a = Accum.getAccum();
public void run() {
for(int x=0; x < 99; x++) {
a.updateCounter(1);
try {
Thread.sleep(50);
} catch(InterruptedException ex) { }
}
System.out.println("two "+a.getCount());
}
}

基本上,updateCounter方法不是线程安全的。如果同时从两个线程调用它,可能会丢失信息。

让我们重写一下,让它更清楚地说明为什么会出现这种情况:

public void updateCounter(int add) {
// Fetch
int originalValue = counter;
// Compute
int newValue = originalValue + add;
// Store
counter = newValue;
}

想象一下,如果两个线程同时进入该方法会发生什么。我们会假装有一些";总排序";发生了什么——现实比这更复杂,但即使是简化的形式也说明了问题。假设counter的值为5,在线程x上我们调用updateCounter(3),在线程y上调用updateCounter(4)。我们可以想象这样一系列的事件:

  1. 线程x执行";获取";操作:originalValue = 5(局部变量,不受线程y影响)
  2. 线程y执行";获取";操作:originalValue = 5
  3. 线程x执行";计算";操作:newValue = 8
  4. 线程y执行";计算";操作:newValue = 9
  5. 线程x执行";存储";操作:counter = 8(注意线程x中的newValue与线程y中的是分开的)
  6. 线程y执行";存储";操作:counter = 9

因此,我们最终得出counter的值为9……就好像updateCounter(3)调用从未发生过一样。如果最后两个操作以相反的顺序发生,则counter将改为8。

解决此问题的方法是使用AtomicInteger类,该类专门设计用于进行类似以下原子的操作:

class Accum {
private final AtomicInteger counter = new AtomicInteger(0);
private static Accum a = new Accum();
private Accum() {
}
public static Accum getAccum() {
return a;
}
public int getCount() {
return counter.get();
}
public void updateCounter(int add) {
counter.addAndGet(add);
}
}

最新更新