非同期の静的メソッドは、静的クラス変数を変更しない場合、スレッドセーフですか?


145

私はあなたがされた静的メソッドがあればと思いましていない同期が、ないではない任意の静的変数を変更し、それは、スレッドセーフでありますか?メソッドがその中にローカル変数を作成する場合はどうでしょうか?たとえば、次のコードはスレッドセーフですか?

public static String[] makeStringArray( String a, String b ){
    return new String[]{ a, b };
}

したがって、2つのスレッドが継続的かつ同時にメソッドを呼び出す場合、1つは犬(「グレートデーン」と「ブルドッグ」など)で、もう1つは猫(「ペルシャ」と「シャム」など)で、猫と犬を取得します同じ配列で?または、猫と犬がメソッドの同じ呼び出し内に同時に存在することはありませんか?


この問題に関する別のスレッド: stackoverflow.com/questions/8015797/...
象嘉道

2
これは別の質問です。これは、静的メソッドの呼び出しがスレッドセーフかどうかであり、配列がそうであるかどうかではありません。
スレッド

回答:


212

このメソッドは100%スレッドセーフであり、そうでなかったとしても安全staticです。スレッド間で問題が発生するのは、スレッド間でデータを共有する必要がある場合です。原子性、可視性などに注意する必要があります。

このメソッドは、スタックに常駐するパラメーターと、ヒープ上の不変オブジェクトへの参照でのみ機能しますスタックは本質的にスレッドに対してローカルであるため、データの共有は発生しません。

不変オブジェクト(Stringこの場合)もスレッドセーフです。一度作成すると変更できず、すべてのスレッドに同じ値が表示されるためです。一方、メソッドが(可変)Dateを受け入れる場合は、問題が発生する可能性があります。2つのスレッドが同じオブジェクトインスタンスを同時に変更すると、競合状態と可視性の問題が発生します。


4
正解。メソッドレベルの変数は、各スレッド実行スタックで複製されます。
シド

技術的にはメソッドはインライン化され、パラメータはCPUレジスタになります。それにもかかわらず、答えは正しいです
bestss

43
スタックはもちろん現在のスレッドに対してローカルですが、そのスタック上の共有オブジェクトへの参照を持つことができます。文字列は不変であるため、この例では問題ではありませんが、渡されたオブジェクトに複数のスレッドからアクセスできる場合、渡されたパラメーターを変更するメソッドにはスレッドセーフの問題が発生する可能性があります。
ジョーンHorstmann

1
@TomaszNurkiewiczが言及したように、可変オブジェクト参照を渡すと、競合状態になる可能性があります。メソッドがオブジェクトを変更しない場合でも、これは当てはまりますか?つまり、オブジェクトが変更可能だったために、競合状態として分類されますか?最後のキーワードをパラメーターに追加するとどうなるでしょうか。
ラファイ2013年

メソッドでクラスオブジェクトを渡すとどうなりますか?スタックまたはヒープ内の変数はありますか?
grep、

28

メソッドは、いくつかの共有状態を変更する場合にのみスレッド安全ではありません。静的であるかどうかは関係ありません。


3
@Konrad_Garusここでの質問は、ローカル変数が共有状態を構成するかどうか、または静的メソッドのスタックがスレッドごとか共有かという線に沿ったものでした。
スレッド

「メソッドは、いくつかの共有状態を変更する場合にのみスレッド安全ではありません。」いいえ、変更せずに共有状態にアクセスするだけの場合は、スレッドに対して安全ではありません。可変オブジェクトへの非同期アクセスは、別のスレッドが適切に同期されている場合でも、オブジェクトが別のスレッドによって変更されている場合、不整合な状態にアクセスする可能性があります。両方のスレッドは、スレッドの安全性を維持するために適切な同期が必要です。
Warren Dew

12

関数は完全にスレッドセーフです。

あなたがそれについて考えるなら...これが違っていたらどうなるかを考えてください。通常の関数はすべて同期しないとスレッド化の問題が発生するため、JDKのすべてのAPI関数は、複数のスレッドによって呼び出される可能性があるため、同期する必要があります。また、ほとんどの場合、アプリは一部のAPIを使用しているため、マルチスレッドアプリは事実上不可能です。

これは馬鹿げすぎて考えられないので、あなただけのために:問題が発生する可能性のある明確な理由がある場合、メソッドはスレッドセーフではありません。私の関数に複数のスレッドがある場合、およびステップデバッガーがあり、次のステップが最初のスレッドを進めた場合、次に2番目のスレッド...おそらく2番目のスレッドが再度ある場合はどうなるかを常に考えてみてください。問題がありますか?見つかった場合、スレッドセーフではありません。

また、ほとんどのJava 1.5 Collectionクラスは、ConcurrentHashMapのように指定されている場合を除き、スレッドセーフではないことにも注意してください。

そして、本当にこれに飛び込みたいなら、volatileキーワードとそのすべての副作用をよく見てください。java.util.ConcurrentでSemaphore()およびLock()クラス、およびそれらの友達を見てください。クラスに関するすべてのAPIドキュメントをお読みください。学び、満足することも価値があります。

この過度に手の込んだ答えでごめんなさい。


2
「それについて考えたら...これが異なる場合にどうなるかを想定してください。通常の関数はすべて、同期しないとスレッドの問題が発生するため、JDKのすべてのAPI関数を同期する必要があります。スレッド。」いい視点ね!
スレッド

1

static同期された静的メソッドでキーワードを使用して、スレッド間で共有される静的データを変更します。ではstatic、キーワード作成されたすべてのスレッドがメソッドの単一バージョンの競合します。

volatile同期されたインスタンスメソッドと共にキーワードを使用すると、各スレッドが共有データの独自のコピーを持ち、スレッド間で読み取り/書き込みがリークしないことが保証されます。


0

文字列オブジェクトが不変であることは、上記のスレッドセーフシナリオのもう1つの理由です。代わりに、可変オブジェクト(たとえばmakeMutableArray ..)を使用すると、スレッドセーフが確実に機能しなくなります。


問題は、静的メソッド呼び出しで、別のスレッドから同じメソッドへの別の呼び出しの引数が表示されるかどうかでした。答えは、反響する「いいえ!」です。これは知っていましたが、疑わしい同僚に証明できるようになりたいと思っていました。
そり

なぜこの回答は反対票が投じられたのですか?他の回答が貢献していない点は、可変性はインバウンドまたはアウトバウンドである可能性があるということです。ミュータブルを返すと、スレッドセーフではなくなります。質問はコメントが示唆するほど狭くはありません。コードサンプルの後の拡張された質問は、おそらくユニットテストとしてコードで表現されている可能性があります。しかし、私は同僚を説得しようとすることに共感しています。「彼らは私や私のテストコード、あるいはジョシュ・ブロックを信じていないが、多分彼らはSOの答えを受け入れるだろう」
som-snytt 2012
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.