web-dev-qa-db-ja.com

Javaでスレッドセーフなシングルトンファクトリを正しく作成するにはどうすればよいですか?

Factoryクラスを書くのはこれが初めてです。以下は私のファクトリクラスです。これがスレッドセーフなシングルトンファクトリクラスを作成する正しい方法かどうかはわかりません。このファクトリを使用してクライアントのインスタンスを返しますか?

public class ClientFactory {

    private static ClientFactory instance = null;   

    private ClientFactory() {

    }

    public static ClientFactory getInstance() {

        if (instance == null)
        {
            instance =  new ClientFactory();
        }

        return instance;
    }

    public IClient getClient() {

        return new TestClient();
    }
}

そして、これが私のTestClientクラスです-

public class TestClient implements IClient {


}

そして、これが私の工場の使い方です-

IClient client = ClientFactory.getInstance().getClient();
10
user2467545

実際、競合状態では複数のClientFactoryをアプリケーションに存在させることができるため、ファクトリはスレッドセーフではありません。 2つのスレッドを想定しましょう:

  1. ThreadAは条件 'if(instance == null)'を評価していて、インスタンスがnullであるため、ステートメントに入ります
  2. ThreadBは条件 'if(instance == null)'を評価しており、インスタンスはnull(Aがインスタンス化を行わなかったため)であるため、ステートメントに入ります。
  3. ThreadAは新しいClientFactory()を作成し、それを返します
  4. ThreadBは新しいClientFactory()を作成し、それを返します
  5. これで、アプリケーションに複数のClientFactoryがあります。もちろん、しばらくしてからインスタンスを取得しようとする他のスレッドは、常に単一のインスタンスを返します。

私の意見では、Javaでシングルトンを書く最も簡単な方法は、列挙型を使用することです。あなたの場合、次のようになります。

public enum ClientFactory {
  INSTANCE;

  public Company getClient() {
    return new Company();
  }
}

そして使用法:

ClientFactory.INSTANCE.getClient()
23
Jakub Kubrynski

Wikiでのスレッドセーフな実装(例) Wikipediaでのシングルトンパターン

上記のリンクのように、単一要素のenum型は、列挙型をサポートする任意のJava)にシングルトンを実装するための最良の方法です。

最高でありながらシンプルなものの1つ:

public class ClientFactory{
    private ClientFactory() {}

    private static ClientFactory INSTANCE=null;

    public static ClientFactory getInstance() {
        if(INSTANCE==null){
            synchronize(ClientFactory.class){
                if(INSTANCE==null) // check again within synchronized block to guard for race condition
                    INSTANCE=new ClientFactory();
            }
        }
        return INSTANCE;
    }
}

Source:ウィキペディア

5
gsndev

シングルトンとファクトリーは別物です。シングルトンをプロパティ構築するには、そのgetInstance()メソッドをファクトリと考えることができると思います。工場は「もの」を作ります。シングルトンとは、これらの「モノ」が常に0個または正確に1個しか存在しないことを意味します。

適切なシングルトンを作成しようとしている場合、Javaでスレッドセーフな方法でこれを行うのは驚くほど面倒です。同期やその他のスレッドセーフな対策がない場合、上記のコードには、ClientFactoryインスタンス変数を初期化するためのcheck-then-setコードの周りに微妙な競合状態があります。この競合状態を回避するには2つの方法があります。どちらを選択するかは、ClientFactoryコンストラクターを通過するのにどれだけの費用がかかるかによって大きく左右されます。私のコンストラクターは通常軽量なので、同期の必要性をすべて一緒に回避するという道を歩みます。

public class ClientFactory {
    private static final ClientFactory instance = new ClientFactory();

    private ClientFactory() { }

    public static ClientFactory getInstance() {
        return instance;
    }
}

誰かが明示的にgetInstance()を呼び出すまで構築せずに、構築で「怠惰」になりたい場合は、競合状態を回避するために同期が必要になります。

public class ClientFactory {
    private static ClientFactory instance = null;

    private ClientFactory() { }

    public static synchronized ClientFactory getInstance() {
        if ( instance == null ) {
            instance = new ClientFactory();
        }
        return instance;
    }
}
3
Bob Kuhar

あなたのファクトリーは完璧なシングルトンです(スレッドセーフではないというだけです)。

2
peter.petrov

clientFactoryはファクトリですが、シングルトンファクトリでもスレッドセーフファクトリでもありません。 ClientFactory.getInstance()。getClinet()が呼び出されると、いつでも新しいインスタンスが返されるため、完全にシングルトンファクトリではありません。このようなメソッドを修正すると、

private IClient iclient;

public IClient getClient() {

    if ( iclient == null ){
         iclient = new TestClient();
    }

    return iclient ;
}

その場合、ファクトリはシングルトンファクトリですが、スレッドセーフではありません。 getInstanceを呼び出すスレッドが複数ある場合、すべてのスレッドがクライアントファクトリインスタンスがnullであることがわかるため、それぞれインスタンスを構築します。問題はgetClient()メソッドでも同じです。

修正は非常に簡単です。これら2つのメソッドを同期済みとして宣言できます。

まず、ファクトリパーターンを本当に使用したい場合は、クライアントのコンストラクタを非表示にすることを忘れないでください

private TestClient(){
}
0
sambean