2

基本的に、オブジェクトを XML ファイルにシリアル化するメソッドと、XML からオブジェクトを読み取るメソッドの 2 つのメソッドを持つクラスがあります。オブジェクトを復元するメソッドの同期部分の例を次に示します。

    public T restore(String from) throws Exception {
     // variables declaration
        synchronized (from) {
            try {
                decoder = new XMLDecoder(new BufferedInputStream(
                        new FileInputStream(from)));
                restoredItem = decoder.readObject();
                decoder.close();
            } catch (Exception e) {
                logger.warning("file not found or smth: " + from);
                throw new Exception(e);
            }
        }
    // try to cast it
    }

オブジェクトをシリアライズするときも、同様のアプローチが取られます。ここで、単体テストを作成すると、各スレッドがブール値または文字列をシリアル化して即座に読み取ろうとする 10 個のスレッドが作成され、ClassCastExceptions が発生したことを示して失敗します。これは、シリアル化が間違っていると思います (シングルスレッド環境ではすべて問題ありません)。この時点まで私と一緒にいてくれた場合:)、ここにあなたの助けが必要な2つの問題があります:

  1. メソッドに渡された文字列引数を同期することは理にかなっていますか (Java に文字列プールがあることを考慮して)? ところで、XMLSerializer クラス自体で同期を試みましたが、結果は同じままでした。
  2. 単一のファイルで io 操作を正しく同期するにはどうすればよいですか?
4

4 に答える 4

5

1. はい、文字列で同期しても問題ありませんが、文字列で同期する必要があります。常に同じオブジェクトを取得するためのintern()

StringBuffer sb = new StringBuffer(); sb.append("a").append("b");
String a = new String(sb.toString());
String b = new String(sb.toString());
a == b; //false
a.equals(b); //true
a.intern() == b.intern(); //true

同じモニターで同期したいので、intern() が必要です。

2. コード内、サードパーティ、または JRE のどこか他の場所で同期される可能性があるため、おそらく文字列で同期したくないでしょう。同期を維持したい場合は、IDクラス(文字列のみを保持できる)を作成し、equals()とhashcode()をオーバーライドして一致させ、キーと値の両方でWeakHashMapに入れます(理由については IdentityHashMap を参照してください)、マップから .get() したもののみを使用します (sync map{ syncKey = map.get(new ID(from)); if syncKey==null create and put new key} sync{syncKey} )。

3. 次に、すべてを一緒に同期するのをやめて、代わりに java.util.concurrent.locks.Lock を使用します。上記と同じセットアップで、ID にロックがアタッチされているだけです。

于 2008-10-12T18:10:49.153 に答える
3

コードの一部が欠落していることを考えると、問題は文字列の同期にあると思います。文字列がプールされていると自由に想定することはできません (同期スキームが壊れる可能性があります)。

最善の方法は、キー (文字列) を実際の同期オブジェクトに関連付けるマップを追加することです。

それ以外は、マルチスレッド テストを試して、失敗の原因を確認することをお勧めします。たとえば、すべてのスレッドが (文字列や beolean ではなく) 文字列値のみを格納するようにした場合でも、テストは失敗しますか?

于 2008-10-12T13:16:12.230 に答える
2

このアプローチには多くの問題があります。

  1. String.intern を呼び出していない限り、from 文字列は、呼び出している他の from 文字列と同じではない可能性があります。内部 Java 文字列キャッシュの動作に依存することは、あまり堅牢ではありません。

  2. 最終ブロックで XMLDecoder を適切に破棄していない場合、その呼び出し中に例外がスローされると、その FileInputStream に関連付けられたファイルの説明がリークされます。

  3. 別の Exception(e) で e をラップする必要はありません。囲んでいるメソッドも Exception をスローすることを宣言しているので、e をスローするだけです。

  4. 例外のキャッチ/スローはコードの匂いです。はい、これは IOException のスーパー クラスであり、XML デコード例外がスローされる可能性がありますが、おそらくキャッチしたくない他の多くのもののスーパークラスでもあります。たとえば、NullPointerException です。

あなたの質問に答えるために、共有ファイルへのアクセスをシリアル化して、複数のスレッドで使用されないようにする方法は難しいです。FileChannel.lock() は JVM 内では機能しません。マシン内の他のプロセスによる変更からファイルをロックするだけです。

私のアプローチは、このクラスからロックを取り除き、コードのスレッド化の問題を認識しているものにラップすることです。

また、ファイル名として文字列ではなくファイルを渡します。これにより、File.createTempFile(2) を使用して、xml を書き込むものと xml を読み取るものとの間に不透明なファイル名を作成できます。

最後に、共有ファイルへのアクセスを同期しますか、それとも同じファイルへの複数のアクセスを検出したときに失敗しますか?

于 2008-10-12T14:13:12.927 に答える
2

String は良いミューテックスにはなりませんが、ミューテックスを作成するために使用できます: Java: Synchronizing on an ID .

于 2008-10-12T17:05:51.720 に答える