在旨在突出竞争条件的作业中,我们获得了以下代码
public class IncreaseDecrease {
public static int IntegerVariable = 0;
public static final int NUM_ITER = 5000000;
public static void main(String[] args) throws Exception {
Increase inc;
Decrease dec;
while (true) {
inc = new Increase();
dec = new Decrease();
inc.start();
dec.start();
inc.join();
dec.join();
System.out.println(IntegerVariable);
IntegerVariable = 0;
Thread.sleep(750);
}
}
}
class Increase extends Thread {
@Override
public void run() {
for (int i = 0; i < IncreaseDecrease.NUM_ITER; i++) {
IncreaseDecrease.IntegerVariable++;
}
}
}
class Decrease extends Thread {
@Override
public void run() {
for (int i = 0; i < IncreaseDecrease.NUM_ITER; i++) {
IncreaseDecrease.IntegerVariable--;
}
}
}
如果每个线程在另一个线程读取之前可以更新该值,则该代码可以打印0,但是由于竞争条件不会发生这种情况,它可以打印-5000000和5000000之间的任何值。
我在windows和repl.it上运行了该代码,它给出了预期的输出:
-310951
-1918567
-3374495
-3219135
-2286639
-3221055
-3794319
-2442047
-2776415
-3617391
但是在Ubuntu上,当我运行它时,它每次都给出0。
我的问题是,为什么会这样? Ubuntu是否以不同的方式管理线程,还是仅仅是我的计算机的特殊情况?
编辑: 在将增量放入不同的方法并向其添加一个操作之后,我观察了竞争条件。这是最终的代码:
public class IncreaseDecrease {
public static int IntegerVariable = 0;
public static final int NUM_ITER = 5000000;
public static void main(String[] args) throws Exception {
Increase inc;
Decrease dec;
while (true) {
inc = new Increase();
dec = new Decrease();
inc.start();
dec.start();
inc.join();
dec.join();
System.out.println(IntegerVariable);
IntegerVariable = 0;
Thread.sleep(750);
}
}
public static void increment ()
{
IntegerVariable++;
double a = Math.pow(3, 7);
}
public static void decrement()
{
IntegerVariable--;
double a = Math.pow(3, 7);
}
}
class Increase extends Thread {
@Override
public void run() {
for (int i = 0; i < IncreaseDecrease.NUM_ITER; i++) {
IncreaseDecrease.increment();
}
}
}
class Decrease extends Thread {
@Override
public void run() {
for (int i = 0; i < IncreaseDecrease.NUM_ITER; i++) {
IncreaseDecrease.decrement();
}
}
}
答案 0 :(得分:1)
我想说明Linux下使用服务器编译器的Hotspot虽然不在Windows上,但更可能的解释是:编译器可以用单个表达式替换整个循环,这就是HotSpot绝对有能力。添加任何本机方法都会使其变得不可能,从而更有可能观察到竞争条件
我猜这也许就是这种情况。
您是否尝试过 IntegerVariable volatile ?这将阻止可能发生的某些编译器优化。
public static volatile int IntegerVariable = 0;
答案 1 :(得分:-1)
对Java中的线程存在一种常见的误解,即它们真正有效地交错处理处理。这实际上并不完全正确,不同系统上的不同JVM的工作方式不同。
这一切都源于JVM决定切换线程的时候。 JVM可能会在遇到Thread.sleep()
或阻塞方法(如synchronized
或lock
时切换线程,但通常如果某个线程没有执行涉及阻塞的任何事情,它会让线程运行。
你的循环在递增和递减值时旋转,没有暂停。如果你在循环中添加Thread.sleep(0)
调用,你可能会看到一个区别,因为你为JVM提供了更多机会来切换你的线程。
for (int i = 0; i < IncreaseDecrease.NUM_ITER; i++) {
IncreaseDecrease.IntegerVariable--;
// Add this.
Thread.sleep(0);
}