2014-10-30 65 views
0

此代码来自样品OCP/SCJPJava线程启动方法或方法

我真的不知道为什么Printx()运行之前被称为()。 以及为什么这是有保证的?

public class ConstructorOrRun extends Thread { 

    private int x = 2; 

    public ConstructorOrRun() throws Exception { 
     x = 5; 
     start(); 
    } 

    public void printX() throws Exception { 
     x = x - 1; 
     System.out.print(x); 
    } 

    public void run() { 
     x *= 2; 
    } 

    public static void main(String[] args) throws Exception { 
     // TODO Auto-generated method stub 
     new ConstructorOrRun().printX(); 

    } 

} 
+2

'extends Thread'是可怕的做法,除非你的意图是改变线程的工作方式。不要那么做。 – cHao 2014-10-30 02:32:35

+0

Nathan Hughes的“这将是糟糕的形式”答案有点轻描淡写。您不能依赖测试来确定程序是否“线程安全”。在一个操作系统版本上测试不会告诉你它是否可以在不同的操作系统版本上运行。在一个JRE版本中测试不会告诉你它是否能够在不同的JRE版本上工作,今天的测试可能不会告诉你它是否会在明天工作。如果一个程序的正确性取决于一场比赛的结果(例如,像在你的例子中设置x的比赛),那么无论测试说什么,该程序都是错误的。 – 2014-10-30 14:07:31

回答

3

ConstructorOrRun()立即在主线程上返回,然后调用printX()。

不能保证在构造函数中调用start()会导致run()开始,更不用说在构造函数返回之前完成(在另一个线程上)。事实上,如果确实如此,我会感到惊讶。

4

我不认为'保证'在这里是正确的词。在实践中,printx可能会首先完成,因为starting a new thread takes a huge amount of time相对于当前正在运行的线程执行小算术运算所需的时间,获取控制台的(无争用)锁并写入该锁。

虽然这是一个竞争条件,但依靠任何一件事情都会是一个非常糟糕的主意。如果一个程序运行足够多的时间,可能会发生各种交织。无法保证首先会发生哪些事情,最好避免做出假设。

还有另一个问题。构造函数由主线程调用,初始化x,然后启动一个新线程。新线程修改run方法中的x,但没有任何要求使x的内容对新线程可见。使x变为volatile将使其内容可见。

还有一个问题:算术运算需要执行多个步骤,并可能被其他线程干扰。这不仅仅是首先发生哪种操作的问题,它们可以交错。解决这个问题需要锁定或使用atomicInteger。

+0

谢谢。这是比其他任何东西都更糟糕的问题...... – user3431327 2014-10-30 14:48:59

+0

@ user3431327:我不知道你在哪里得到这个,但是这些问题测试了具体的目标,这对我来说并不明显。 – 2014-10-31 02:03:58