0

我想测试延迟初始化是否是线程安全的,所以我的代码如下:

package LazyInit;

import java.util.Random;

public class UnThreadSafeLazyInit {

    private ExpensiveObject instance = null;

    public ExpensiveObject getInstance() {
        if (null == instance) {
            instance = new ExpensiveObject();
        }
        System.out.println("instance=" + instance);
        return instance;
    }

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub


        for (int i = 0; i < 5; i++) {
            UnThreadSafeLazyInit  init = new UnThreadSafeLazyInit();
            Task t1 = init.new Task();
            Task t2 = init.new Task();
            t1.start();
            t2.start();
            try {
                Thread.sleep(4000);
            } catch (Exception e) {
                e.printStackTrace();
            }
            System.out.println(t1.getInstance() == t2.getInstance());
        }
    }

    static class ExpensiveObject {

    }

    class Task extends Thread {

        private ExpensiveObject instance = null;
        private Random rand = new Random(47);

        public void setInstance () {
            this.instance = UnThreadSafeLazyInit.this.getInstance();
        }

        public ExpensiveObject getInstance() {
            return instance;
        }

        @Override
        public void run() {
            // TODO Auto-generated method stub

            try {
                Thread.sleep(rand.nextInt(1000));
            } catch (Exception e) {
                e.printStackTrace();
            }
            setInstance();
        }
    }

}

在我的代码中,每次我new调用两个 Thead 任务public ExpensiveObject getInstance(),以证明这两个instance可能不一样引用 ExpensiveObject 以来的竞争条件。当我运行它时,它总是返回true. t1.getInstance() == t1.getInstance()据我所知,如果我不synchronized使用该函数public ExpensiveObject getInstance(),它可能会返回 false,因为延迟初始化中存在竞争条件。我需要找出哪个代码是错误的。谢谢你。

4

4 回答 4

2

它不是线程安全的,只需检查代码即可。您遇到的问题是,几毫秒的延迟对于计算机来说是一个巨大的时间,您非常非常不可能看到这种类型的测试有问题。

例如,更新 volatile 字段与其他线程可见之间的典型延迟约为 5 纳秒。大约在这么长的时间里,您的解决方案不是线程安全的。您正在等待长达 1,000,000,000 纳秒的时间来查看是否有问题。

这就像试图查看持续 5 秒的烟花是否熄灭,但闭上眼睛 317 年才得出没有烟花的结论。

于 2013-05-12T08:44:20.300 回答
1

为什么它不是线程安全的已经被其他人介绍过了。但我想评论你的标题:“我想测试延迟初始化是否是线程安全的”。

您无法测试一段代码是否是线程安全的。您也许可以找到一个测试来证明它不是,但仅测试并不能证明线程安全:

  • 您的测试可能不会以重现问题的方式交错线程
  • System.out.println您的测试可能会引入隐藏实际问题的额外同步(例如同步)
  • 该问题可能仅出现在少数测试运行可能不会遇到的非常罕见的情况下
  • 该问题可能仅出现在某些 JVM / CPU 上,并且您的测试在一种特定设置下“有效”这一事实无论如何都不能证明任何事情
于 2013-05-12T09:05:28.417 回答
0

最简单的方法是让 ExpensiveObject 成为一个非常昂贵的对象:

public class ExpensiveObject {
    public ExpensiveObject() {
        System.out.println("I'm expensive!");
        try {
            Thread.sleep(2000L);
        }
        catch (InterruptedException e) {
        }
        System.out.println("See. It took 2 seconds to create me!");
    }
}

否则,进入rece 条件的机会非常小,特别是因为一个线程在另一个线程之后启动,因此在另一个线程之后调用setInstance()

于 2013-05-12T08:28:48.537 回答
0

它不是线程安全的。这次你很幸运。修改您的代码:

public ExpensiveObject getInstance() {
    if (null == instance) {
        System.out.println("old instance=" + instance);
        instance = new ExpensiveObject();
        System.out.println("new instance=" + instance);
    }
    return instance;
}
// In main
Thread.sleep(40); // Thread.sleep(4000);
// In run
Thread.sleep(rand.nextInt(10)); // Thread.sleep(rand.nextInt(1000));

false我在控制台中看到很多带有此代码的内容。

于 2013-05-12T08:57:36.613 回答