Javaシングルトンクラスのスレッドセーフティ

シングルトンは、アプリケーションによって作成されるオブジェクトを制限するために広く使用される生成デザインパターンの一つです。マルチスレッド環境で使用する場合、シングルトンクラスのスレッドセーフが非常に重要です。実際のアプリケーションでは、データベース接続やエンタープライズ情報システム(EIS)のようなリソースが制限されており、リソース不足を避けるために賢明に使用する必要があります。これを実現するために、シングルトンデザインパターンを実装できます。リソース用のラッパークラスを作成し、ランタイムで作成されるオブジェクトの数を1つに制限できます。

Javaでのスレッドセーフシングルトン

通常、シングルトンクラスを作成するには、以下の手順に従います:

  1. 新しいオブジェクトの作成を避けるためにプライベートなコンストラクタを作成します。
  2. 同じクラスのプライベートなstaticインスタンスを宣言します。
  3. 提供するパブリックスタティックメソッドは、シングルトンクラスのインスタンス変数を返します。変数が初期化されていない場合は初期化し、そうでない場合は単にインスタンス変数を返します。

上記の手順を使用して、次のように見えるシングルトンクラスを作成しました。ASingleton.java

package com.journaldev.designpatterns;

public class ASingleton {

	private static ASingleton instance = null;

	private ASingleton() {
	}

	public static ASingleton getInstance() {
		if (instance == null) {
			instance = new ASingleton();
		}
		return instance;
	}

}

上記のコードでは、getInstance()メソッドはスレッドセーフではありません。複数のスレッドが同時にアクセスできます。最初の数個のスレッドでは、インスタンス変数が初期化されていない場合、複数のスレッドがifループに入り、複数のインスタンスが作成されます。これはシングルトンの実装を壊します。

シングルトンクラスでスレッドセーフを実現する方法は?

スレッドセーフを実現する方法は3つあります。

  1. クラスのローディング時にインスタンス変数を作成します。
    長所:
  • 同期なしでスレッドセーフ
  • 実装が容易

短所:

  • アプリケーションで使用されない可能性のあるリソースの早期作成
  • クライアントアプリケーションは引数を渡せないため、再利用できません。たとえば、クライアントアプリケーションがデータベースサーバーのプロパティを提供する汎用のシングルトンクラスを持っている場合。
  1. getInstance()メソッドを同期化します
    長所:
  • Thread safety is guaranteed.
  • クライアントアプリケーションはパラメータを渡すことができます。
  • 遅延初期化が達成されました。

デメリット:

  • ロックのオーバーヘッドによるパフォーマンスの低下。
  • 不要な同期化が、インスタンス変数が初期化された後には不要です。
  1. ifループ内で同期化ブロックとボラタイル変数を使用します。
    メリット:
  • スレッドセーフが保証されます。
  • クライアントアプリケーションは引数を渡すことができます。
  • 遅延初期化が達成されました。
  • 同期化オーバーヘッドは最小限で、変数がヌルの場合にのみ最初の数スレッドに適用されます。

デメリット:

  • 余分なif条件。

スレッドセーフを達成するための3つの方法を見てみると、3番目の方法が最良の選択肢だと考えます。その場合、変更されたクラスは次のようになります。

package com.journaldev.designpatterns;

public class ASingleton {

	private static volatile ASingleton instance;
	private static Object mutex = new Object();

	private ASingleton() {
	}

	public static ASingleton getInstance() {
		ASingleton result = instance;
		if (result == null) {
			synchronized (mutex) {
				result = instance;
				if (result == null)
					instance = result = new ASingleton();
			}
		}
		return result;
	}

}

ローカル変数resultは不要に見えますが、コードのパフォーマンスを向上させるために存在しています。ほとんどの場合、インスタンスが既に初期化されている場合、ボラタイルフィールドは一度だけアクセスされます(「return instance;」の代わりに「return result;」が原因)。これにより、メソッド全体のパフォーマンスが最大25%向上する可能性があります。これを実現するためのより良い方法があるか、または上記の実装でスレッドセーフが損なわれている場合は、コメントして共有してください。

ボーナスのヒント

文字列は、`synchronized` キーワードと一緒に使用するのにはあまり適していません。これは、文字列が 文字列プールに格納されており、別のコードによって使用されている可能性がある文字列をロックしたくないためです。そのため、オブジェクト変数を使用しています。Java における同期と スレッドセーフティーについて詳しく学んでください。

さらに、当社の GitHub リポジトリからさらに多くの Java の例をご覧いただけます。

Source:
https://www.digitalocean.com/community/tutorials/thread-safety-in-java-singleton-classes