我是编程新手,研究线程已经有一段时间了。
因此,下列程式码应该会给予的输出为:
one 98098
two 98099
有时候确实如此。
当我试着运行它几次时,它给出了不同的输出。我可以理解JVM控制线程,我不能直接影响它,但是一些输出小于98,000,即使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());
}
}
1条答案
按热度按时间ggazkfy81#
基本上,你的
updateCounter
方法不是线程安全的,如果同时从两个线程调用它,你可能会丢失信息。让我们重写一下,让它更清楚地说明为什么会出现这种情况:
想象一下,如果两个线程同时进入该方法,会发生什么情况。我们将假设发生的事情存在某种“总排序”--实际情况比这更复杂,但即使是简化形式也会显示问题。假设
counter
的值开始为5,在线程x上我们调用updateCounter(3)
,在线程y上我们调用updateCounter(4)
。我们可以想象这一系列事件:1.线程x执行“fetch”操作:
originalValue = 5
(局部变量,不受线程y影响)1.线程y执行“fetch”操作:
originalValue = 5
1.线程x执行“计算”操作:
newValue = 8
1.线程y执行“计算”操作:
newValue = 9
1.线程x执行“存储”操作:
counter = 8
(请注意,线程x中的newValue
与线程y中的newValue
是分开的)1.线程y执行“存储”操作:
counter = 9
个因此,我们最终得到
counter
的值为9......就好像updateCounter(3)
调用从未发生过一样。如果最后两个操作以相反的顺序发生,那么counter
将改为8。解决此问题的方法是使用
AtomicInteger
类,该类专门设计用于执行类似于 atomic 的操作: