2012-06-26 107 views
1

为什么下面的程序输出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的值应该对测试器线程可见?

+5

价值我甚至不明白为什么你认为它应该打印11 –

+1

这是一个有趣的问题!它不能打印11或12它甚至不会编译!差点拿到我们 –

+0

我不明白你为什么认为它应该打印除'0'之外的东西? –

回答

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

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()后不等待,在打印前值线程增加它。

0

,我一直在寻找的主要答案是项之前发生:概念详述如下:

http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/package-summary.html#MemoryVisibility

所以说,在文档 - 一)之前线程开始 - 所有语句之前的语句 b)线程执行中的所有语句都在新线程终止时完成。

与上述理解 - 我应该始终可见的新线程开始。这应该是 - 在任何时候,所有系统 - 所以当启动被称为和一个新的线程启动时,它应该总是看到我为1.

但是,我们打印i值的时间越来越由主线程执行 - 而不是由测试器线程执行。因此,即使新的Tester线程可能会将值看作1并将其增加为2 - 在main中的执行并不反映它,因为i ++不是原子操作。

现在假设我们尝试做的INT为:

private volatile int i; 

挥发性担保的之前发生关系,不仅对具体的变量,但也声明,直到它。

获得执行的主线程的println可能会在增量甚至开始之前得到执行。所以我们可能会看到有11个打印出来,即使在变量变化之后。类似的情况下存在使变量为AtomicInteger。

调用时会看到增加值中的run方法:

System.out.println("i "+ i.incrementAndGet()); 

但不是主线程。 运行方法/主要方法中的数据可见性不同。 所使用的实例变量对于两个正在执行的线程都是相同的。

+0

好的,现在接受你自己的答案。我辞职继续编辑我的。 – Theraot

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++; 
    } 

} 
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变量之间共享2个不同步的线程。

因此i变量的某个值不会在线程之间更新。 您可以通过使用volatile关键字

private volatile int i; 

同步或同步的代码段增加i

public void run(){ 
    synchronized(this){ 
     i++; 
    } 
}