为什么以下程序输出11而不是12? 线程不使用相同的实例变量吗?请解释一下?
public class Tester extends Thread {
private int i;
public static void main(String[] args){
Tester t = new Tester();
t.run();
System.out.print(t.i);
t.start();
System.out.print(t.i);
}
public void run(){ i++;}
}
上面的代码编译得很好。我在构造对象时默认为0值。 在关系概念发生之前,在线程开始之前执行的所有代码都已完成。 这个概念是 - 实例变量在多个线程之间共享 - 这里有两个线程在运行 - 主线程和Tester线程。所以我应该与两个线程共享? - 如果i是共享的并且如果发生 - 在启动Tester线程之前保持关系之前,那么增量i的值应该对Tester线程可见?
答案 0 :(得分:3)
给新线程增加变量的时间,尝试使用
public static void main(String[] args){
Tester t = new Tester();
t.run();
System.out.println(t.i);
t.start();
try {
Thread.sleep(1000); // 1 sec
} catch (Exception ex) {}
System.out.println(t.i);
}
代码中唯一的问题是您打印t.i
值并且不等待t.start()
后,在线程增加之前打印该值。
答案 1 :(得分:2)
如果允许实例变量可以被多个线程访问,例如将其公开,或者通过任何其他方式。
在你的代码中会发生这样的事情:Tester
线程t
将访问它自己的变量,你也将从主线程访问同一个变量。当您要求它打印该值时,它可以打印Testet
线程t
此刻的任何值。
当主线程调用run
方法时,它将在主线程中执行,有效地将字段的值增加到1(默认值为0)。之后,当您调用方法start
时,它将启动单独的线程,Java VM将调用方法run
,然后字段再次增加,这次从1增加到2。
所以,我希望输出为1然后,可能为1或可能为2,具体取决于在您要求从主线程打印值之前线程执行的时间....确切的结果你得到取决于你的机器,在另一台电脑,它可能是另一个故事。这取决于CPU,操作系统,可用内存和其他东西。
在dash1e中建议使用his answer,您可以使用Thread.sleep(1000);
使主线程等待Tester
线程t
在后台执行。这大大增加了Tester
线程t
在主线程要求打印之前更新字段值的可能性。也就是说,我想明确表示使用Thread.sleep(1000);
等待任务完成本身就不够好......如果你愿意,可以在一段时间内拨打Thread.sleep
电话在哪里验证是否符合某个条件,即spinning。
您可以从主线程打印字段的值这一事实说明您可以从另一个线程访问它。这是一件好事,因为你希望你的线程以某种方式进行沟通。
可以访问它,因为它只是一个int,并且int不能处于无效状态,因此不需要同步访问。虽然你可能得到一个不是最新的值,但它会在后台改变,所以它不是很可靠。
如果您想要一个只能由单个线程加入的值,并且每个线程都存在一个非常友好的值,那么请查看ThreadLocal。
答案 2 :(得分:0)
我正在寻找的主要答案是以前发生的事情:概述如下:
http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/package-summary.html#MemoryVisibility
所以它在文档中说 - a)在线程开始之前 - 完成之前的所有语句 b)线程执行中的所有语句都在新线程终止时完成。
按照上述理解 - 我应该始终可见新线程开始。这应该是 - 在任何时候,在所有系统上 - 所以当调用start并启动新线程时,它应该始终将i视为1。
但是,我们打印i值的时间是由主线程执行 - 而不是由Tester线程执行。因此,即使新的Tester线程可能将值视为1并将其增加为2 - main中的执行也不会反映它,因为i ++不是原子操作。
现在假设我们尝试将int设为:
private volatile int i;
不稳定保证发生在关系之前,不仅是特定变量,还包括声明直到它。
执行的主线程的println可能会在增量开始之前执行。因此,即使在变量变为volatile之后,我们也可能会看到11被打印出来。类似的情况使变量成为AtomicInteger。
调用时的run方法将看到递增的值:
System.out.println("i "+ i.incrementAndGet());
但不是主线程。 run方法/ main方法中的数据可见性不同。 两个执行的线程使用的实例变量相同。
答案 3 :(得分:0)
您已启动该主题,但尚未等待它停止。使用t.join()
等待完成。而且,是的,你有线程同步问题,但这是另一个问题。
public class Tester extends Thread {
private int i;
public static void main(String[] args) throws InterruptedException {
Tester t = new Tester();
t.run();
System.out.println(t.i);
t.start();
t.join();
System.out.println(t.i);
}
public void run() {
i++;
}
}
答案 4 :(得分:0)
我的代码:
public class Tester extends Thread {
private int i;
public static void main(String[] args){
Tester t = new Tester();
t.run();
System.out.print(t.i);
t.start();
System.out.print(t.i);
}
public void run(){ i++;}
}
你调用t.run()和t.start()。有2个线程正在运行t.run()
线程和t.start()
线程。
在i
变量中,您在两个不同步的线程之间共享。
因此有时候i
变量的值不会在线程之间更新。
您可以使用volatile关键字
private volatile int i;
或同步代码段会增加i
public void run(){
synchronized(this){
i++;
}
}