我想知道java线程中不安全的递减/递增的结果,所以有我的程序:
主要课程:
public class Start {
public static void main(String[] args) {
int count = 10000000, pos = 0, neg = 0, zero = 0;
for (int x=0; x<10000; x++) {
Magic.counter = 0;
Thread dec = new Thread(new Magic(false, count));
Thread inc = new Thread(new Magic(true, count));
dec.start();
inc.start();
try {
inc.join();
dec.join();
} catch (InterruptedException e) {
System.out.println("Error");
}
if (Magic.counter == 0)
zero++;
else if (Magic.counter > 0)
pos++;
else
neg++;
}
System.out.println(Integer.toString(neg) + "\t\t\t" + Integer.toString(pos) + "\t\t\t" + Integer.toString(zero));
}
}
线程类:
public class Magic implements Runnable {
public static int counter = 0;
private boolean inc;
private int countTo;
public Magic(boolean inc, int countTo) {
this.inc = inc;
this.countTo = countTo;
}
@Override
public void run() {
for (int i=0;i<this.countTo;i++) {
if (this.inc)
Magic.counter++;
else
Magic.counter--;
}
}
}
我已经运行了几次程序,并且总是得到更积极的结果然后是否定的。我也试图改变哪些线程开始的顺序,但这没有改变。一些结果:
Number of results < 0 | Number of results > 0 | Number of results = 0
1103 8893 4
3159 6838 3
2639 7359 2
3240 6755 5
3264 6728 8
2883 7112 5
2973 7021 6
3123 6873 4
2882 7113 5
3098 6896 6
答案 0 :(得分:6)
我打赌你会看到完全相反的行为与下面的改变(即,反转分支而不改变其他任何东西):
if (this.inc)
Magic.counter--; // note change, and lie about `this.inc`
else
Magic.counter++;
如果是真的,这表明这有什么表明线程交互?
现在,为了好玩,让Magic.counter
易变 - [怎么]结果发生变化?
如何删除volatile
并使用if/else
围绕lock
? (lock
确保完整的记忆围栏并建立一个关键区域。它应该总是产生完美的结果。)
快乐的编码。
需要考虑的事项:
Magic.counter
变量的可见性中,很多背景中缺少volatile / memory-fence。 (我相信符合标准的JVM实际上可能会产生更糟糕的结果......)++
和--
运算符本质上是非原子的。答案 1 :(得分:0)
一般来说,这是由于Java内存模型的工作方式。您正在访问两个不同线程中的共享变量而没有同步。变量声明为volatile,也不是atomar操作。
缺少协调和atomar或volatile变量将导致JVM在执行时完成的线程代码的内部优化。此外,未超过内存屏障的非易失性变量(即synchronized
)将导致每个线程的缓存值 - 因此在其缓存中有两个冲突的线程局部副本。
鉴于Java中缺少顺序一致性模型,复杂的运行时优化以及所使用的JVM和底层系统(单核或多核,超线程)的特性,不可能确定性地预测结果 - 仅仅因为它是违反了Java语言模型的几种多线程约定。在同一台机器上运行完全相同的代码可能仍会导致类似的结果,但由于线程调度的影响,其他操作系统进程的CPU使用等,它们可能不会完全相同。
以下是有关JMM的一些资源:http://www.cs.umd.edu/~pugh/java/memoryModel/