Java シングルトン パターンと最適化されたマルチスレッド タスク処理についての深い理解

シングルトン モードでは、複数のインスタンスを作成することなく、プログラム内にクラスのインスタンスが 1 つだけ存在することが保証され、グローバル アクセス ポイントが提供されます。

ハングリーモード

クラスがロードされると、インスタンスが作成されます。
ここに画像の説明を挿入します

class  Singleton {
    
    
    private static final Singleton instance = new Singleton();
    //将构造方法设为私有,以防止外部通过new关键字创建新的实例。
    private Singleton() {
    
    }
    public static Singleton getInstance() {
    
    
        return instance;
    }
}
  • 上記のコードは、Singleton というクラスを定義します。
  • プライベート静的定数インスタンスがクラス内に定義されており、これが Singleton クラスの唯一のインスタンスです。
  • Singleton クラスの唯一のインスタンスを取得するために、パブリック静的メソッド getInstance() が提供されています。

レイジーモード

インスタンスはクラスのロード時には作成されませんが、初めて使用されるときに作成されます。
ここに画像の説明を挿入します

シングルスレッドバージョン

class Singleton {
    
    
    private static Singleton instance = null;
    private Singleton() {
    
    
    }
    public static Singleton getInstance() {
    
    
        if (instance == null) {
    
    
            instance = new Singleton();
        }
        return instance;
    }
}

マルチスレッド版

上記のシングルスレッド コードはマルチスレッドでエラーを引き起こします。getInstance()複数のスレッドが同時にメソッドを呼び出すと、複数のインスタンスが作成される可能性があり、安全ではありません。ここでは、問題を解決するためのメソッドにキーワードをgetInstance()追加するだけです。synchronized

class Singleton {
    
    
    private static Singleton instance = null;
    private Singleton() {
    
    
    }
    public synchronized static Singleton getInstance() {
    
    
        if (instance == null) {
    
    
            instance = new Singleton();
        }
        return instance;
    }
}

ダブルチェックロック

class Singleton {
    
    
    //volatile关键字保证了instance变量在多线程环境下的可见性。
    private static volatile Singleton instance = null;
    private Singleton() {
    
    }
    public synchronized static Singleton getInstance() {
    
    
        if (instance == null) {
    
    
            synchronized (Singleton.class){
    
    
                if (instance == null ){
    
    
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

二重チェックは次のように理解できます:
最初の if でインスタンスが作成されたかどうかが最初に判断され、作成されていない場合は最初の if に入り、1 つのスレッドがロックを正常に取得します (残りのスレッドはブロックして待機します)。となり、スレッドが再びロックを取得し、インスタンスが作成されているかどうかを判定し、作成されていない場合はインスタンスを作成します。このインスタンスが作成されると、ロックを競合する他のスレッドは内部 if によってブロックされ、他のインスタンスは作成されなくなります。

ブロックキュー

ブロッキング キューはスレッド セーフなデータ構造にすることができ、次の特徴があります。

  • キューがいっぱいの場合、キューに入り続けると、他のスレッドがキューから要素を取得するまでブロックされます。
  • キューが空の場合、デキューを続行すると、他のスレッドが要素をキューに挿入するまでブロックされます。

ブロッキング キューの典型的なアプリケーション シナリオは、プロデューサー/コンシューマー モデルです。

ブロッキング キューは Java 標準ライブラリに組み込まれています。一部のプログラムでブロッキング キューを使用する必要がある場合は、標準ライブラリのキューを直接使用できます。

  • BlockingQueue はインターフェイスであり、実際のクラスは LinkedBlockingQueue です
  • put メソッドはエンキューをブロックするために使用され、take はデキューをブロックするために使用されます。
  • BlockingQueue には、offer、pol、peek などのメソッドもありますが、これらのメソッドにはブロック特性がありません。

ブロッキングキューを実装してみましょう。

  • 循環キューを通過
  • ロック制御に同期を使用する
public class BlockingQueue {
    
    
    private int[] arr = new int[1000];
    private volatile int size = 0;
    private int tail = 0;
    private int head = 0;

    public void put(int value) throws InterruptedException {
    
    
        synchronized (this) {
    
    
            while (size == arr.length) {
    
    
                wait();
            }
            arr[tail] = value;
            tail = (tail + 1) % arr.length;
            size++;
            notifyAll();
        }
    }

    public int take() throws InterruptedException {
    
    
        int ret = 0;
        synchronized (this) {
    
    
            while (size == 0) {
    
    
                wait();
            }
            ret = arr[head];
            head = (head + 1) % arr.length;
            size--;
            notifyAll();
        }
        return ret;
    }

    public static void main(String[] args) throws InterruptedException {
    
    
        BlockingQueue bq = new BlockingQueue();
        Thread t1 = new Thread(() -> {
    
    

            try {
    
    
                for (int i = 0; i < 10; i++) {
    
    
                    bq.put(i);
                    System.out.println("生产者放入:" + i);
                    Thread.sleep(1000);
                }
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
        });
        t1.start();
        Thread t2 = new Thread(() -> {
    
    

            try {
    
    
                for (int i = 0; i < 10; i++) {
    
    
                    int num = bq.take();
                    System.out.println("消费者取出:" + num);
                    Thread.sleep(1000);
                }
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
        });
        t2.start();
        t1.join();
        t2.join();
    }
}

ここに画像の説明を挿入します

おすすめ

転載: blog.csdn.net/st200112266/article/details/132995333