非线程安全的代码意外地给出了可重复的结果

时间:2016-03-12 12:46:06

标签: java multithreading thread-safety

这是关于测试的问题。我已经多次运行代码并得到相同的答案:999999.他们说这个代码每次都不会产生相同的结果,即使在编写runLoop方法时存在synchronized。我错过了什么?

public class B extends Thread{
    static int a = 0;

    public static void main(String[] Args){
        B MyB = new B();
        MyB.start();
        runLoop(1000000);
        System.out.println(a);
    }

    public static synchronized void runLoop(int b){
        for(int i = 0; i<b; i++){
            a=i;
        }
    }

    public void run(){
        runLoop(12345678);
    }
}

4 个答案:

答案 0 :(得分:1)

通常在你打电话时

SomeThread.start();

正确启动线程需要一些时间,这允许执行此命令的线程执行其下一行中的一些,例如

SomeThread.start();//lets say it should print "A"
System.out.print("B");

在许多情况下会打印BA而不是AB,这就是您的问题所在。

因此,如果您的代码在MyB线程start()之前,主线程可以调用自己的runLoop(1000000);。然后MyB可以运行runLoop(12345678);但是现在主线程可以执行System.out.println(a);,它以非同步的方式访问a,因此它可以显示MyB在当前迭代的次数a时间点(因此两个线程同时访问999999,每次运行应用程序时可能会产生不同的结果。)

如果您声称自己总是得到回复MyB.start();,那么您非常幸运,或者没有向我们展示您的真实代码(例如runLoop(1000000);MyB.start()之间的情况其他代码可能需要足够的时间让TimeUnit.SECONDS.sleep(1);完成proc rollon {boxes args} { foreach box $boxes { eval {$box yview} $args } } }。

答案 1 :(得分:0)

您错过了 无法 通过多次运行来证明您的代码具有确定性的事实。但 可以 证明它不是确定性的。这就是编写多线程代码的问题:根据个人偏见来解释结果太容易了。我们都这样做。

此处还有其他内容:代码通过start方法间接调用runLoop(),这是正确的, 代码直接调用runLoop()主要方法。这就是为什么代码不是确定性的 - 这是原始问题的答案。 main方法中对runLoop()的显式调用不应该存在。所有主要的应该是踢开线程。

答案 2 :(得分:0)

在推断线程安全问题时,很可能它适用于您的计算机。或者在许多机器上,在正常情况下。但这并不意味着该程序正常工作。实际上,“总是”工作的代码可能会在某些条件下中断。

答案 3 :(得分:0)

这里有两件事 -

  1. 单线程模型 - 要确保获得值999999,您应该限制线程同时访问它。在这种情况下,它的主线程和一个正在启动。您可以按照前面的答案中的建议同步课程{{1}}。

  2. 多线程模型 - 因为您只有同步的类的实例,所以仍可以同时访问类级资源。在此输出中保证始终相同。