4
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();
    }
}

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

Foo@24c21495----thread1
Foo@24c21495----thread2

或者

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

...

为什么?

4

4 回答 4

5

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

  • 您打印的第一件事就是结果。各种代码都可能在这里进行 JIT 编译,并可能初始化其他资源。我相信这有效地充当了固有的同步点。
  • 你的睡眠很短,而且很均匀,这无济于事。

基本上,我相信两个线程都在创建新实例,然后都显示结果——除非你“幸运”,否则两个线程都会看到第二个执行分配的线程的结果。

如果你只是添加:

System.out.println("Starting");

从一开始main,您有时会看到不同的输出。如果您还将睡眠更改为:

Thread.sleep((long) (Math.random() * 100));

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

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

于 2013-06-03T06:07:17.837 回答
0

因为你sleep(1)只睡了 1 毫秒。当第二个线程命中getInstance你的时候,你foo已经被初始化了。

您可能想要的是调用一个synchronizedinit 函数,该函数foo在设置之前检查是否为 null。根本不需要睡觉。它仍然会输出相同的内容,但将是一个线程安全的初始化程序。

例子:

public static Foo getInstance () {
  if( foo == null )
    initFoo();
  return foo;
}

private static synchronized void initFoo () {
  if( foo == null )
    foo = new Foo();
}
于 2013-06-03T06:07:52.627 回答
0

我相信在一个单一的classloaderstatic fields总是共享的。

于 2013-06-03T06:12:24.403 回答
0

如果您想要一个“每个线程单例”,那么您可能想要查看 ThreadLocal [1]。然后,您也不必负责正确同步对 getInstance 的调用。

[1] http://docs.oracle.com/javase/6/docs/api/java/lang/ThreadLocal.html

于 2013-06-03T06:12:41.510 回答