单例类方法的并发调用

Luk*_*uky 6 java concurrency singleton multithreading singleton-methods

我有一个单身人士课程:

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);
            }
    }
}
Run Code Online (Sandbox Code Playgroud)

多个线程正在调用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();
}
}
Run Code Online (Sandbox Code Playgroud)

我注意到两个Threads同时运行,就像两个工作函数同时被实例化一样.

我想要运行最后一个线程来代替前一个线程,而不是同时运行.是否有可能在java中使第二个调用覆盖第一个调用的内存空间?

Boh*_*ian 16

您的getSingleton()方法试图懒惰地初始化 SINGLETON实例,但它有以下问题:

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

最好和最简单的是安全地懒惰地初始化单例而不进行同步如下:

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

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

这是线程安全的,因为java类加载器的契约是所有类在它们可以被使用之前完成它们的静态初始化.此外,类加载器在引用之前不会加载类.如果两个线程getSingleton()同时调用,Holder该类仍将只加载一次,因此new Singleton()只执行一次.

这仍然是懒惰的,因为Holder该类仅从getSingleton()方法引用,因此Holder只有在第一次调用时才会加载该类 getSingleton().

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


这种代码模式是与单身一起飞行的唯一方式.它是:

  • 最快(没有同步)
  • 最安全的(依靠工业强度级装载机安全)
  • 最干净的(最少的代码 - 双重检查锁定是丑陋的,它做了很多行)


其他类似的代码模式(同样安全和快速)是使用enum单个实例,但我发现这是笨拙的,意图不太明确.


Jim*_*ner 5

正如@amit 在评论中所述,您的getSingleton()方法应该是synchronized. 这样做的原因是多个线程可能同时请求一个实例,第一个线程仍然会初始化对象,并且在下一个线程检查时引用将为空。这将导致创建两个实例。

public static synchronized Singleton getSingleton() {
    if (istance == null)
        istance = new Singleton();
    return istance;
}
Run Code Online (Sandbox Code Playgroud)

synchronized将您的方法标记为将导致它阻塞并且一次只允许一个线程调用它。这应该可以解决您的问题。