10

次のように HashMap をラップするクラスがあるとします。

public final class MyClass{

     private final Map<String, String> map;

     //Called by Thread1
     public MyClass( int size ){
         this.map = new HashMap<String, String>( size );
     }

     //Only ever called by Thread2
     public final String put( String key, String val ){
        return map.put( key, value );
     }

     //Only ever called by Thread2
     public final String get( String key ){
        return map.get( key );
     }

     //Only ever called by Thread2
     public final void printMap(  ){
       //Format and print the contents of the map
     }

}

このクラスは「Thread1」を介して初期化されます。ただし、put、get、printMap およびその他の操作は、「Thread2」によってのみ呼び出されます。

このクラスが次のようにスレッドセーフであることを理解しているのは正しいですか。

  1. マップへの参照は final と宣言されているため、他のすべてのスレッドはマップの初期状態を参照します (happens-before が確立されます)。

  2. put/get/printMap/etc は Thread2 によってのみ呼び出されるため、相互排除の必要はありません。

ありがとうございました

4

4 に答える 4

4

JLS にはクラスがスレッドセーフであるという概念が含まれていないため、これに答えるのは少し難しいです。JLS が指定するのは、アクション間の関係 (フィールドへの書き込み、フィールドからの読み取りなど) だけです。

とはいえ、ほとんどの定義では、このクラスはスレッドセーフではありません。スレッドセーフではない Map への非同期アクセスによって引き起こされるデータ競合が含まれています。ただし、構築後にスレッド 2に安全に発行し、その時点で 1 つのスレッドによってのみアクセスされるため、スレッド セーフはスレッド セーフです。この場合、スレッド セーフは問題になりませんthis.mapthis.map

言い換えれば、これは、HashMap1 つのスレッド内でのみ作成およびアクセスされた場合にスレッドセーフかどうかを尋ねるよりも、わずかに複雑です。その場合の答えは、スレッドセーフではHashMapありませんが、そうである必要はありません。

同様に、あなたのクラスはスレッド セーフではありませんが、そうである必要はないように思えます。

于 2013-12-20T19:01:58.630 に答える
0

何が起こるかの定義に立つ場合、実際にはスレッドセーフになります。つまり、thread-2 だけが Map からの書き込みと取得を行います。

Map は final と宣言されているため、スレッド 1 の書き込みとスレッド 2 の読み取りに関して、事前発生の関係を確立します。

a) マップへの参照が final と宣言されているため、他のすべてのスレッドはマップの初期状態を参照します (happens-before が確立されます)。

はい。指定された空の HashMap size

b) put/get/printMap/etc は Thread2 によってのみ呼び出されるため、相互排除の必要はありません。

はい、同様ですが、このタイプのロジックは通常、実際には私を怖がらせます:)

于 2013-12-20T18:39:28.693 に答える
-1

ここでの質問は、クラスの実装そのものではなく、記述したクラスの操作がスレッドセーフかどうかに関するものなので、クラスの実装はあまり重要ではありません。

Thread2 がMyClassインスタンスにアクセスする 2 つの方法が考えられます。

  1. Thread1 と Thread2 はどちらも独立して実行されます。この場合、Thread1 で実行されているコンストラクターが終了する前に Thread2 が put/get/printMap/etc を呼び出し、NPE が発生する可能性があるため、スレッドセーフではありません。Thread2MyClassがアクセスするインスタンスは、Thread2 がアクセスする方法によっては null になる可能性があります。共有インスタンスの場合、Thread2 がアクセスすると、MyClassコンストラクターが Thread1 で実行を終了していない場合は null になる可能性があります。
  2. Thread1 はインスタンスを作成し、このインスタンスを Thread2 に渡します。この場合、スレッドセーフですが、Thread2 はインスタンスが渡されるのを待たなければならないため、このインスタンスへのマルチスレッド アクセスがないため、これは実際には無関係です。

したがって、あなたが説明した状況では、答えは実際にはクラスの実装ではなく、そのインスタンスの共有方法に依存します。最悪のシナリオ (方法 1) では、スレッドセーフではありません。

于 2013-12-20T18:25:43.537 に答える