我有一个最喜欢的C#程序,类似于下面的程序,它表明如果两个线程共享相同的内存地址进行计数(一个线程递增n次,一个线程递减n次),你可以获得非零的最终结果。只要n相当大,就很容易让C#在[-n,n]之间显示一些非零值。但是,即使将线程数增加到1000(500 up,500 down),我也无法让Java产生非零结果。是否有一些内存模型或规格差异与C#我不知道保证这个程序总是会产生0,尽管我不知道调度或核心数量?我们是否同意即使我们无法通过实验证明该程序也能产生非零值?
(不是:,我在here上找到了这个确切的问题,但是当我运行该主题的代码时,我也得到零。)
public class Counter
{
private int _counter = 0;
Counter() throws Exception
{
final int limit = Integer.MAX_VALUE;
Thread add = new Thread()
{
public void run()
{
for(int i = 0; i<limit; i++)
{
_counter++;
}
}
};
Thread sub = new Thread()
{
public void run()
{
for(int i = 0; i<limit; i++)
{
_counter--;
}
}
};
add.run();
sub.run();
add.join();
sub.join();
System.out.println(_counter);
}
public static void main(String[] args) throws Exception
{
new Counter();
}
}
答案 0 :(得分:4)
您给出的代码只在单个线程上运行,因此总是会得到0的结果。如果您实际上启动两个线程,您确实可以获得非零结果:< / p>
// Don't call run(), which is a synchronous call, which doesn't start any threads
// Call start(), which starts a new thread and calls run() *in that thread*.
add.start();
sub.start();
在试运行中我的盒子上给了-2146200243。
答案 1 :(得分:2)
假设你真的是start
,而不是run
。
在大多数常见平台上,它很可能会产生非零,因为++
/ --
在多核的情况下不是原子操作。在单核/单CPU上,您很可能会得到0因为++
/ --
是原子的,如果编译为一条指令(add
/ inc
),但该部分依赖于JVM
在此处查看结果:http://ideone.com/IzTT2
答案 2 :(得分:0)
您的程序存在的问题是您没有创建OS线程,因此您的程序基本上是单线程的。在Java中,您必须调用Thread.start()
来创建新的OS线程,而不是Thread.run()
。这与初始Java API中令人遗憾的错误有关。这个错误是设计师让Thread
实现了Runnable
。
add.start();
sub.start();
add.join();
sub.join();