アプリケーションのさまざまなユーティリティクラスで特に使用される静的メソッドのスレッドセーフを確保するための一般的な方法やルールの出口はありますか。ここで、Webアプリケーションのスレッドセーフを具体的に指摘します。
パラメーターとして不変オブジェクトを使用する静的メソッドはスレッドセーフであり、可変オブジェクトはそうではないことはよく知られています。
Java.util.Date
を操作するユーティリティメソッドがあり、そのメソッドがJava.util.Date
のインスタンスを受け入れる場合、このメソッドはスレッドセーフではありません。次に、パラメータの受け渡し方法を変更せずにスレッドセーフにする方法は?
public class DateUtils {
public static Date getNormalizeDate(Date date) {
// some operations
}
}
クラスjavax.faces.context.FacesContext
も変更可能ですか?このクラスのインスタンスをそのような静的ユーティリティメソッドに渡すことはスレッドセーフですか?
インスタンスがパラメータとして渡される場合と渡されない場合があるこのクラスのリストは、長くなる可能性があります。そのようなユーティリティクラスのコードを記述するとき、どの点に留意する必要がありますか?
不変オブジェクトをパラメーターとして持つ静的メソッドはスレッドセーフであり、可変オブジェクトはそうではないことはよく知られています。
私はこれに異議を唱えるでしょう。メソッドに渡される引数は、スレッドごとのイディオムであるスタックに格納されます。
パラメーターがDate
などの可変オブジェクトである場合、他のスレッドが他の場所で同時に変更していないことを確認する必要があります。しかし、それはメソッドのスレッドセーフとは無関係の別の問題です。
投稿したメソッドはスレッドセーフです。状態を維持せず、引数のみで動作します。
Javaの並行性の実践 、またはJavaのスレッドセーフに関する同様の本を読むことを強くお勧めします。いくつかのStackOverflowの回答では適切に対処できない複雑なテーマです。
クラスはメンバー変数を保持しないため、メソッドはステートレス(ローカル変数と引数のみを使用)であり、したがってスレッドセーフです。
それを呼び出すコードはスレッドセーフではないかもしれませんが、それは別の議論です。たとえば、Dateはスレッドセーフではありません。呼び出し元のコードが別のスレッドによって書き込まれたDateを読み取る場合、Dateの書き込みおよび読み取りコードで適切な同期を使用する必要があります。
JVMの構造を考えると、ローカル変数、メソッドパラメーター、および戻り値は本質的に「スレッドセーフ」です。ただし、クラスを適切に設計する場合、インスタンス変数とクラス変数はスレッドセーフになります。もっと こちら
私が考える方法は次のとおりです。CampSite(静的メソッド)を想像してください。キャンピングカーとして、リュックサックにたくさんのオブジェクトを持ち込むことができます(これはスタックで渡される引数です)。 CampSiteは、テントやキャンプストーブなどを置く場所を提供してくれますが、CampSiteでできるのが自分のオブジェクトを変更できるようにするだけなら、スレッドセーフです。 CampSiteは、薄い空気(FirePit firepit = new FirePit();
)から物を作成することもでき、これもスタック上に作成されます。
いつでも私は私のすべてのオブジェクトを私のラックスタックに入れて消えることができ、他のキャンパーのいずれかが現れることができます。このCampSiteの異なるスレッドは、他のスレッドでCampSiteを作成したスタック上のオブジェクトにアクセスできません。
CampStoveが1つだけあるとします(CampStoveの単一オブジェクトであり、個別のインスタンス化はありません)。想像力の広がりによってCampStoveオブジェクトを共有している場合、マルチスレッドの考慮事項があります。私はキャンプストーブの電源を入れ、消えて、他のキャンピングカーが電源を切った後に再び現れたくありません-私のホットドッグが行われたかどうか、永遠に確認します。どこかに同期を配置する必要があります... CampStoveクラス、CampSiteを呼び出していたメソッド、またはCampSite自体に... Duncan Jones のような問題」。
non-static CampSiteオブジェクトの個別のインスタンス化でキャンプしている場合でも、campStoveの共有には同じマルチスレッドの考慮事項があることに注意してください。
メソッドが開始したらすぐにその(可変)オブジェクトのコピーを作成し、元のパラメーターの代わりにコピーを使用することをお勧めします。
このようなもの
public static Date getNormalizeDate(Date date) {
Date input = new Date(date.getTime());
// ...
}
たくさんの回答がありますが、その理由を実際に指摘している人はいません。
したがって、これは次のように考えることができます。スレッドが作成されるたびに、独自のスタックで作成されます(作成時のスタックのサイズは約2MBです)。したがって、実際に発生する実行は、このスレッドスタックのコンテキスト内で発生します。作成された変数はすべてヒープに存在しますが、参照はスタックに存在しますが、例外はスレッドスタックに存在しない静的変数です。
あなたが行う関数呼び出しは、静的であろうと非静的であろうと、実際にスレッドスタックにプッシュされます。メソッド全体がスタックにプッシュされたため、発生する変数の作成はスタック内に残り(例外は静的変数)、1つのスレッドのみがアクセスできます。
したがって、静的変数の状態を変更するまで、すべてのメソッドはスレッドセーフです。