簡體   English   中英

單例類方法的並發調用

[英]concurrent calls of singleton class 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);
            }
    }
}

多個線程正在調用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();
}
}

我注意到兩個Threads同時運行,就像兩個工作函數同時被實例化一樣。

我想要運行最后一個線程來代替前一個線程,而不是同時運行。 是否有可能在java中使第二個調用覆蓋第一個調用的內存空間?

你的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()方法引用,因此只有在第一次調用getSingleton()時才會加載Holder類。

不需要同步,因為此代碼依賴於類加載器的內部同步,這是防彈。


這種代碼模式是與單身一起飛行的唯一方式。 它是:

  • 最快(沒有同步)
  • 最安全的(依靠工業強度級裝載機安全)
  • 最干凈的(最少的代碼 - 雙重檢查鎖定是丑陋的,它做了很多行)


其他類似的代碼模式(同樣安全和快速)是使用帶有單個實例的enum ,但我發現這是笨拙的並且意圖不太明確。

正如@amit在評論中所說,你的getSingleton()方法應該是synchronized 這樣做的原因是多個線程可能同時請求一個實例,第一個線程仍然在初始化對象,並且在下一個線程檢查時引用將為null。 這將導致創建兩個實例。

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

將方法標記為已synchronized將導致它阻止並且一次只允許一個線程調用它。 這應該可以解決您的問題。

在工廠方法上使用synchronized

public class Singleton {
    private static Singleton istance = null;

    private final Singleton() {} // avoid overrides

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

    public void work() { // not static, otherwise there's no need for the singleton
        // ...
    }
}

或者,簡單地說,使用私有的最終初始化程序(實例化將在類加載時發生)

public class Singleton {
    private static final Singleton istance = new Singleton(); // class-load initialization

    private final Singleton() {} 

    public static Singleton getSingleton() { // no need for synchronized
        return istance;
    }

    public void work() { 
        // ...
    }
}

Java Concurrency In Practice中給出的資源持有者: http//www.javaconcurrencyinpractice.com/是可用的最佳非阻塞單例模式。 單例被懶惰地初始化(當第一次調用getInstance()方法時,SingletonHolder和Singleton類在運行時加載)並且訪問或方法是非阻塞的。

public class SingletonFactory {

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

public static Singleton getInstance() {
    return SingletonFactory.SingletonHolder.instance;
}

static class Singleton{
}

}

我想出了這個代碼,它正在做我需要的東西。 原始問題是“可以在不使用線程的情況下執行以下操作嗎?而是使用該語言直接操作內存?” 如果答案是否定的,也許你可以幫我改進以下內容:

public class Main {
private static Thread t;
public static void main(String[] args) {
    work();
    for (int i =0;i<100; i++);
    System.out.println("oooooooooooooooooooooooooooooooooooooooooo");
    for (int i =0;i<100; i++);
    work();
    for (int i =0;i<500; i++);
    System.out.println("oooooooooooooooooooooooooooooooooooooooooo");
}

public static void work(){
    if (t != null) t.interrupt();
    t= new Thread (new Runnable(){
            public void run(){
                // Pause for 4 seconds
                try {
                    Thread.sleep(600);
                } catch (InterruptedException e) {
                    // We've been interrupted: no more messages.
                    return;
                }
                for(int i=0; i<10000; i++){
                    System.out.println(i);
                }
            }
            });
    t.start();
}
}

此代碼對於“去抖”對偵聽器的多次調用非常有用,可以在用戶輸入的突發中觸發。 它具有使用睡眠功能的缺點。 睡眠時間應該足夠高,以防止突發事件開始執行耗時的任務(只有最后一個事件應該)。 不幸的是,即使是很長的睡眠時間也無法保證這種情況總能發生。

您可以在共享資源周圍使用鎖定。 使用Reentrant類。 它可以防止多個線程的競爭條件。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM