シングルトン モードでは、複数のインスタンスを作成することなく、プログラム内にクラスのインスタンスが 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();
}
}