Java单例模式在多线程中输出相同的对象?

liu*_*jun 4 java singleton

public class Foo {

    private static  Foo foo;
    private Foo(){}
    public static Foo getInstance(){
        if (foo==null){
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {}
            foo = new Foo();
            System.out.println(foo+"----"+Thread.currentThread().getName());
        }
        return foo;
    }

    public static void main(String[] args) throws Exception {

        Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() { 
                Foo foo1 = Foo.getInstance();
            }
        },"thread1");

        Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                Foo foo2 = Foo.getInstance();
            }
        },"thread2");
    thread1.start();
    thread2.start();
    }
}
Run Code Online (Sandbox Code Playgroud)

我只想模拟这些代码在多线程环境中是不安全的,但它总是输出两个相同的对象,如:

Foo@24c21495----thread1
Foo@24c21495----thread2
Run Code Online (Sandbox Code Playgroud)

要么

Foo@3d4b7453----thread1
Foo@3d4b7453----thread2

...
Run Code Online (Sandbox Code Playgroud)

为什么?

Jon*_*eet 5

我相信这里有两个综合原因:

  • 你打印的第一件事就是结果.各种代码可能在这里进行JIT编译,并且可能初始化其他资源.我相信这是一个有效的固有同步点.
  • 你的睡眠很短,而且均匀,这没有用.

基本上,我相信两个线程都在创建新实例,然后两个都显示结果 - 除非你"幸运",否则两个线程都会看到任何线程执行第二个任务的结果.

如果您只是添加:

System.out.println("Starting");
Run Code Online (Sandbox Code Playgroud)

在一开始main,你有时会看到不同的输出.如果你也改变你的睡眠:

Thread.sleep((long) (Math.random() * 100));
Run Code Online (Sandbox Code Playgroud)

...那么你更有可能看到它.基本上,您希望一个线程在另一个线程处于休眠状态时完成整个"休眠,初始化,打印".

另一种选择是简单地向构造函数添加一个print语句:即使两个线程在当前诊断位置显示相同的对象,您仍然会看到构造的两个实例,这表明代码是不安全的.