4

私は約1か月間Javaを使用していますが、それでも一般的にプログラミングのアマチュアです。何か問題が発生した場合は、遠慮なく修正してください。多分私はいくつかの余分な詳細を提供しますが、私は今とても混乱しているので、もう何が重要かを決めることができません。

それで、私はマルチスレッドのクライアントサーバーアプリケーションを開発してきました。すべてのスレッドは同じオブジェクトを使用しており、特定の構成値と共有ロガーが格納されています。このオブジェクトはサーバースレッドで初期化され、引数としてクライアントスレッドクラスコンストラクターに渡されます。最初は、サーバーの起動時にそのオブジェクトのフィールドが1回だけ変更されると想定されていたため、同時アクセスについて心配する必要はありませんでしたが、変更時に一部の構成値を構成ファイルから再読み取りする必要があります。サーバーを再起動します。

いくつかの調査の後で頭に浮かんだ最初のアイデアは、クラスからのいくつかの値が要求されたときに呼び出され、最後のアクセス以降に構成ファイルが変更された場合はそれらの値を再読み取りし、そうでない場合はすぐに戻る同期メソッドを作成することでした。これ:

<This code is inside "config" class, instance of which is shared between threads>
private static long lastModified;
private static File configFile;

public class ChangingVariableSet
    {
    <changing variables go here>
    }

private synchronized void ReReadConfig
    {
    long tempLastMod = configFile.lastModified();
    if(lastModified == tempLastMod)
        return;
    <reread values here>
    lastModified = tempLastMod;
    }

public ChangingVariableSet GetValues()
    {
    ReReadConfig();
    <return necessary values>
    }

(上記のコードはテストされていません。一般的な考え方を理解したいだけです)。

しかし、値が要求されるたびにブロックするというアイデアは好きではありませんでした。それは高価に思えるので、私のアプリケーションは将来、大量のスレッドでかなり高負荷になる可能性があります。したがって、私は「良い」アイデアを思いつきました。ロックする前にファイルが変更されているかどうかを確認してから、ロックされたメソッド内で再度チェックし、可能な限りロックを回避することです。

 public ChangingVariableSet GetValues()
    {
    if(lastModified == configFile.lastModified())
        ReReadConfig();
    <return necessary values>
    }

10分後、それがダブルチェックロックと呼ばれることを学び、さらに10分後、この記事を読んだ後、私の世界は2回崩壊しました。 /floatタイプはアトミックではありません。それとも、オブジェクトの作成が含まれないので、結局は機能しますか?また、longでの操作は非アトミックであるため、「lastModified」を揮発性として宣言するだけで本当に十分でしょうか。可能であれば、なぜそれが機能する/機能しないのかについて適切な説明をしたいと思います。前もって感謝します。

PS:同様の質問がすでに数回回答されていることは知っています。「ReReadConfig」ではなく、「getValue」メソッド全体をニッチピッキングして同期するのをやめたほうがいいかもしれませんが、スレッドセーフプログラミングと将来同様のことを避けるために、自分のコードに落とし穴を見つけるため。また、文法やスペルの間違いの可能性についてもお詫びします。英語はあまりよくわかりません。


編集:最初に、最後の「if」句のタイプミスを修正しました。2番目-警告、上記のコードはスレッドセーフではありません。使用しないでください!方法で

 public ChangingVariableSet GetValues()
    {
    if(lastModified == configFile.lastModified())
        ReReadConfig();
    <return necessary values>
    }

if-checkと値が返されるまでの期間にファイルが更新された場合、スレッドAが値を返し始める前にスレッドBがReReadConfigを開始する可能性があり、必要なデータに危険な部分的な変更が発生します。過度のブロッキングなしで必要なことを行う正しい方法は、ReentrantReadWriteLockを使用することですが、それでも、過度の(そして高価なファイルは大きなXMLであると想定される)構成の再読み取りを回避するためにダブルチェックを使用したいと思います。

<...>
private static final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
private static final Lock read  = readWriteLock.readLock();
private static final Lock write = readWriteLock.writeLock();

private void ReReadConfig
    {
    write.lock();
    long tempLastMod = configFile.lastModified();
    if(lastModified == tempLastMod)
        return;
    <reread values here>
    lastModified = tempLastMod;
    write.release();
    }

 public ChangingVariableSet GetValues()
    {
    if(lastModified == configFile.lastModified())
        ReReadConfig();
    read.lock();
    <get necessary values>
    read.release();
    <return necessary values>
    }

今では少なくともスレッドセーフに見えますが、チェックするときに揮発性の「lastModified」変数に応じて質問が開いたままになります。揮発性変数は非アトミック操作で何も保証できないことをどこかで読んだことがあります。書き込みは非アトミックです。

4

2 に答える 2

5

ReadWriteLockを使用したい。ライターがいない限り、これはリーダーをブロックしません。

于 2012-05-04T18:19:48.217 に答える
1

すべての構成データが単一の不変オブジェクトにあり、そのオブジェクトへの共有揮発性参照があるようにコードを整理できる場合、これはスレッドセーフになります。この種のユースケースは、実際にはvolatileセマンティクスの改訂が目指していたものです。

public static volatile Config config;

void rereadConfig() {
  if (modified)
    config = new Config(...);
}
于 2012-05-04T20:44:15.297 に答える