3

翻訳ユーティリティを提案するクラスがあります。翻訳自体は 30 分ごとに再読み込みする必要があります。そのためにSpring Timerサポートを使用しています。基本的に、私のクラスは次のようになります。

public interface Translator {
    public void loadTranslations();
    public String getTranslation(String key);
}

loadTranslations() は実行にかなり時間がかかる可能性があるため、実行中も古い翻訳を利用できます。これは、翻訳をローカル Map にロードし、すべての翻訳がロードされたときに参照を変更することによって行われます。

私の問題は次のとおりです。スレッドがすでに翻訳をロードしているときに、2 番目のスレッドも実行しようとすると、2 番目の更新を開始せずに、それを検出してすぐに戻るようにするにはどうすればよいですか。

同期されたメソッドは、ロードをキューに入れるだけです...私はまだJava 1.4を使用しているため、java.util.concurrentはありません。

ご協力いただきありがとうございます !

4

4 に答える 4

3

なんらかの形式のロック メカニズムを使用して、タスクがまだ進行中でない場合にのみタスクを実行します。ロック トークンの取得は、1 ステップのプロセスである必要があります。見る:

/**
 * @author McDowell
 */
public abstract class NonconcurrentTask implements Runnable {

    private boolean token = true;

    private synchronized boolean acquire() {
        boolean ret = token;
        token = false;
        return ret;
    }

    private synchronized void release() {
        token = true;
    }

    public final void run() {
        if (acquire()) {
            try {
                doTask();
            } finally {
                release();
            }
        }
    }

    protected abstract void doTask();

}

タスクが同時に実行された場合に例外をスローするコードをテストします。

public class Test {

    public static void main(String[] args) {
        final NonconcurrentTask shared = new NonconcurrentTask() {
            private boolean working = false;

            protected void doTask() {
                System.out.println("Working: "
                        + Thread.currentThread().getName());
                if (working) {
                    throw new IllegalStateException();
                }
                working = true;
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                if (!working) {
                    throw new IllegalStateException();
                }
                working = false;
            }
        };

        Runnable taskWrapper = new Runnable() {
            public void run() {
                while (true) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    shared.run();
                }
            }
        };
        for (int i = 0; i < 100; i++) {
            new Thread(taskWrapper).start();
        }
    }

}
于 2008-09-30T11:06:16.383 に答える
1

私は.netのバックグラウンドから来ました(Javaの経験はまったくありません)が、メソッドの開始時にすでに実行されているかどうかをチェックする、ある種の単純な静的フラグを試すことができます。次に、そのフラグの読み取り/書き込みが同期されていることを確認するだけです。したがって、最初にフラグをチェックし、設定されていない場合は設定し、設定されている場合は戻ります。設定されていない場合は、残りのメソッドを実行し、完了したら設定を解除します。コードを try/finally に入れ、フラグ iunsetting を finally に入れて、エラーが発生した場合に常に設定解除されるようにしてください。非常に単純化されていますが、これで十分かもしれません。

編集:これは実際には、メソッドを同期するよりもうまく機能する可能性があります。完成前の翻訳の直後に新しい翻訳が本当に必要なのですか?また、しばらく待機する必要がある場合、スレッドを長時間ロックしたくない場合があります。

于 2008-09-30T07:57:16.633 に答える
0

これは、実際には、従来の方法で実行されたときにシングルトン(gasp!)の構築を管理するために必要なコードと同じです。

if (instance == null) {
  synchronized {
    if (instance == null) {
       instance = new SomeClass();
    }
  }
}

内側のテストは外側のテストと同じです。外側のテストは、同期ブロックに定期的に入ることがないようにするためのものです。内側のテストは、最後にテストを行ってから状況が変化していないことを確認するためのものです(同期に入る前にスレッドがプリエンプトされた可能性があります)。

あなたの場合:

if (translationsNeedLoading()) {
  synchronized {
    if (translationsNeedLoading()) {
       loadTranslations();
    }
  }
}

更新:シングルトンを構築するこの方法は、JDK1.4では確実に機能しません。説明については、ここを参照してください。ただし、このシナリオでは大丈夫だと思います。

于 2008-09-30T08:17:29.673 に答える
0

ロード スレッドのハンドルを保持して、実行中かどうかを確認しますか?

または、同期フラグを使用して、ロードが進行中かどうかを示すことはできませんか?

于 2008-09-30T07:55:50.287 に答える