一尘不染

单例类方法的并发调用

java

我有一个单例课程:

public class Singleton {
    private static Singleton istance = null;

    private Singleton() {}

    public synchronized static Singleton getSingleton() {
        if (istance == null)
            istance = new Singleton();
        return istance;
    }

    public void work(){
            for(int i=0; i<10000; i++){
                Log.d("-----------", ""+i);
            }
    }
}

并且多个线程正在调用work()函数:

public class Main {

public static void main(String[] args) {

    new Thread (new Runnable(){
        public void run(){
            Singleton s = Singleton.getSingleton();
            s.work();}
    }).start();

    System.out.println("main thread");

    new Thread(new Runnable() { 
         public void run() {
             Singleton s = Singleton.getSingleton();
                s.work();
         }
    }).start();
}
}

我注意到两个线程正在同时运行,就好像两个工作函数是同时实例化的一样。

我希望最后一个线程代替上一个线程运行,而不是同时运行。在Java中是否可以使第二个调用覆盖第一个调用的内存空间?


阅读 329

收藏
2020-12-03

共1个答案

一尘不染

您的getSingleton()方法正在尝试延迟初始化
SINGLETON实例,但存在以下问题:

因此竞争条件AMY会导致创建两个实例。

最好和最简单的方法是安全地懒惰地初始化一个单例 而不 同步,如下所示:

private static class Holder {
    static Singleton instance = new Singleton();
}

public static Singleton getSingleton() { // Note: "synchronized" not needed
    return Holder.instance;
}

这是线程安全的,因为java类加载器的约定是所有类在使用之前都必须完成其静态初始化。同样,类加载器在被引用之前不会加载类。如果getSingleton()同时调用两个线程,则Holder该类仍将仅加载一次,因此new Singleton()将仅执行一次。

这仍然是懒惰的,因为Holder类是 从引用getSingleton()的方法,所以Holder在第一次呼叫时类只会被加载
getSingleton()而成。

不需要同步,因为此代码依赖于类加载器的内部同步,这是防弹的。

此代码模式是单例飞行的唯一方法。它是:

  • 最快(无同步)
  • 最安全(取决于工业强度等级的装载机安全性)
  • 最干净的(最少的代码-双重检查的锁定很丑陋,并且它的功能很多行)

另一种类似的代码模式(既安全又快速)是在enum单个实例中使用,但是我发现这样做很笨拙,目的并不明确。

2020-12-03