Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/349.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java单例模式在多线程中输出两个相同的对象?_Java_Singleton - Fatal编程技术网

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

Java单例模式在多线程中输出两个相同的对象?,java,singleton,Java,Singleton,我只想模拟这些代码在多线程环境中是不安全的,但它总是输出两个相同的对象,如: public class Foo { private static Foo foo; private Foo(){} public static Foo getInstance(){ if (foo==null){ try { Thread.sleep(1); } catch (Interrupt

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

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


为什么?

我认为这里有两个综合原因:

  • 打印的第一件事就是结果。所有类型的代码都可能在这里进行JIT编译,并可能初始化其他资源。我相信这是一个内在的同步点,有效地
  • 你的睡眠时间很短,而且很均匀,这对你没有帮助
基本上,我相信两个线程都在创建新实例,然后都在显示结果——除非你“幸运”,否则两个线程都会看到第二个执行赋值的线程的结果

如果您只是添加:

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

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

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

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

因为您的
睡眠(1)
仅睡眠1毫秒。当第二个线程点击
getInstance
时,您的
foo
已经初始化

您可能需要调用
synchronized
init函数,该函数在设置前检查
foo
是否为null。根本不需要睡觉。它仍将输出相同的值,但将是线程安全的初始值设定项

例如:

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

我相信在单个
类加载器中,
静态字段总是共享的

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


[1]

因为foo在我的机器上是静态的,所以它在第一次运行时返回了不同的对象。尝试更多的运行。如果您尝试得更多,它将生成不同的对象。我认为调用
System.out.println(foo+“----+Thread.currentThread().getName())
的速度足够慢,不能让两个线程都显示它们自己的结果。因为从输出中我们可以看出两个线程都进入了if块,这基本上意味着两个线程都在构造新对象。但在印刷过程中,这两家公司都在印刷最新的产品。我可能想尝试打印
Foo-temp
而不是
Foo
;foo=温度并打印
temp
,而不是
foo
。依我看,如果(一些棘手的)重新排序没有弄糟的话,这应该会显示预期的输出。我想你已经错过了问题的要点。提供的代码不是线程安全的:它应该是一个单实例,而不是因为缺少任何类型的同步。尽管如此,在上面的设置中,两个线程可能会获得两个不同的Foo实例:只需充分增加睡眠时间。然而,如果这是我们的意图,那么这绝对不是一个实现它的好方法。@JonSkeet我认为这个问题不够具体,我们中的任何人都无法确切地知道所问的问题。@Shadow Grover:我个人认为这很清楚:“我只想模拟这些代码在多线程环境中是不安全的。”@你母语是什么?作为一个以英语为母语的人,我不知道他们在问什么。我可以猜,但我不能保证我的猜测是正确的。我的诊断经验表明你的第一句话是错误的,至少在我的机器上是这样。正在创建两个实例-但在打印结果时,两个线程已充分“同步”,它们都在打印相同的值。OP并没有试图提出线程安全的初始化(有更简单的方法),他试图证明当前代码是不安全的。@JonSkeet是的,这两种结果都是可能的。但必须进行更好的调试才能确定发生了什么。谁知道在我的机器上发生的同样的事情是否也发生在你的机器上。如果两个线程睡眠时间相同,这可能不会产生相同的结果。+1。获得两个不同的实例或
Foo
完全取决于机会,因此为了增加机会,您的建议是有意义的。将是确保(至少在理论上并尽可能地)线程同时到达某个执行点。@PeterLawrey:是的,它仍然可能产生不期望的结果-但它将大大提高发现问题的机会。
Thread.sleep((long) (Math.random() * 100));
public static Foo getInstance () {
  if( foo == null )
    initFoo();
  return foo;
}

private static synchronized void initFoo () {
  if( foo == null )
    foo = new Foo();
}