27

現時点では単一のスレッドでのみ使用されるクラスを作成する場合、現時点では必要がなくても、それらをスレッドセーフにする必要がありますか? 後でこのクラスを複数のスレッドで使用する可能性があり、その時点で競合状態が発生する可能性があり、最初にクラスをスレッドセーフにしなかった場合、競合状態を見つけるのに苦労する可能性があります。または、パフォーマンスを向上させるために、クラスをスレッドセーフではないものにする必要がありますか? しかし、時期尚早の最適化は悪です。

別の質問: 必要に応じてクラスをスレッドセーフにする必要がありますか (複数のスレッドで使用する場合、そうでない場合)、必要に応じてこの問題を最適化する必要がありますか (同期が処理時間の重要な部分を消費することがわかっている場合)?

両方の方法のいずれかを選択した場合、デメリットを軽減する方法はありますか? または、使用する必要がある 3 番目の可能性はありますか?

編集:この質問が頭に浮かんだ理由を述べます。当社では、データをプロパティ ファイルに書き込む非常に単純なユーザー管理を作成しました。私はそれをWebアプリで使用しましたが、いくつかの作業を行った後、ユーザー管理がユーザーのプロパティ(名前とパスワードを含む)とロールを忘れたという奇妙なエラーが発生しました。それは非常に厄介でしたが、一貫して再現可能ではなかったので、競合状態だったと思います. ディスクからの読み取りとディスクへの書き込みのすべてのメソッドを同期したので、問題はなくなりました。そもそも同期を使ってクラスを書いていれば、おそらくすべての面倒を避けることができたのではないかと思いましたか?

EDIT 2 : Pragmatic Programmer のヒントを見ていると、ヒント #41: Always Design for Concurrency を見ました。これは、すべてのコードがスレッド セーフであるべきだと言っているわけではありませんが、並行性を念頭に置いて設計する必要があることを示しています。

4

13 に答える 13

29

以前はすべてをスレッドセーフにしようとしていましたが、「スレッドセーフ」の意味は使用法に依存することに気付きました。多くの場合、その使用法を予測することはできません。呼び出し元はスレッドセーフな方法で使用するために何らかのアクションを実行する必要があります。

最近では、ほとんどすべてをシングル スレッドを想定して書いており、スレッドに関する知識は、重要ないくつかの場所にのみ当てはめています。

そうは言っても、(適切な場合) 不変型も作成します。これは、自然にマルチスレッド化に適しているだけでなく、一般的に推論するのも簡単です。

于 2008-10-24T16:53:13.287 に答える
22

データから始めます。明示的に共有するデータを決定し、それを保護します。可能であれば、ロックをデータでカプセル化します。既存のスレッドセーフな並行コレクションを使用します。

可能な限り、不変オブジェクトを使用してください。属性を final にし、コンストラクターで値を設定します。データを「変更」する必要がある場合は、新しいインスタンスを返すことを検討してください。不変オブジェクトはロックを必要としません。

共有またはスレッド制限されていないオブジェクトについては、時間をかけてスレッドセーフにしないでください。

コード内の期待を文書化します。JCIP アノテーションは、利用可能な事前定義済みの最良の選択肢です。

于 2008-10-25T04:27:35.070 に答える
6

「できるだけシンプルに、しかしシンプルにしない」という原則に従います。要件がなければ、それらをスレッドセーフにするべきではありません。そうすることは投機的であり、おそらく不必要です。スレッドセーフ プログラミングでは、クラスがさらに複雑になり、同期タスクのためにパフォーマンスが低下する可能性があります。

オブジェクトがスレッドセーフであると明示的に述べられていない限り、そうではないことが期待されます。

于 2008-10-24T17:00:26.377 に答える
4

私は個人的に、必要なときにのみ「スレッドセーフ」なクラスのみを設計します。必要な場合にのみ最適化するという原則に基づいています。Sunは、シングルスレッドコレクションクラスの例でも同じように進んでいるようです。

ただし、変更することにした場合にどちらの方法でも役立ついくつかの優れた原則があります。

  1. 最も重要なこと:同期する前に考えてください。かつて同僚がいて、「万が一に備えて、同期の方が良いはずだ」と同期していたことがあります。これは間違っており、複数のデッドロックバグの原因でした。
  2. オブジェクトが不変である可能性がある場合は、それらを不変にします。これは、スレッド化に役立つだけでなく、マップなどのキーとして、セットで安全に使用するのに役立ちます。
  3. オブジェクトをできるだけシンプルに保ちます。それぞれが理想的には1つの仕事だけをするべきです。メンバーの半分へのアクセスを同期したい場合は、オブジェクトを2つに分割する必要があります。
  4. java.util.concurrentを学び、可能な限りそれを使用してください。彼らのコードは、99%のケースであなた(または私のもの)よりも良く、速く、そして安全になります。
  5. Javaでの並行プログラミングを読んでください、それは素晴らしいです!
于 2008-10-24T17:19:22.577 に答える
3

補足として、同期!=スレッドセーフ。それでも、データを同時に変更することはできませんが、同時に読み取ることはできます。したがって、Javaメモリモデルを念頭に置いてください。同期とは、データの同時変更を保護するだけでなく、すべてのスレッドでデータの信頼性を高めることを意味します。

そして、はい、私の意見では、スレッドセーフは最初から組み込まれている必要があり、並行性の処理が必要かどうかはアプリケーションロジックに依存します。何も想定しないでください。テストが正常であるように見えても、競合状態は眠っている犬です。

于 2009-03-12T02:54:09.800 に答える
2

どのクラスがスレッドセーフであるかを宣言するには、JCIPアノテーションが非常に便利であることがわかりました。私のチームは、クラスに@ThreadSafe@NotThreadSafeまたは@Immutableのアノテーションを付けています。これは、Javadoc を読む必要があるよりもはるかに明確であり、FindBugsは @Immutable および@GuardedBy契約の違反を見つけるのにも役立ちます。

于 2008-10-25T04:04:57.127 に答える
1

現時点では単一のスレッドでのみ使用されるクラスを作成する場合、それらをスレッドセーフにする必要があります

プログラム全体がスレッドセーフであるために、スレッドによって使用されるクラス自体がスレッドセーフである必要はありません。非「スレッドセーフ」クラスのオブジェクトが適切な同期によって保護されていれば、スレッド間で安全に共有できます。したがって、それが明らかになるまで、クラス自体をスレッドセーフにする必要はありません。

ただし、マルチスレッドはプログラムの基本的な (アーキテクチャ上の) 選択です。後付けで追加するものではありません。そのため、どのクラスをスレッドセーフにする必要があるかを最初から知っておく必要があります。

于 2011-11-03T13:20:01.760 に答える
1

これが私の個人的なアプローチです:

  • 可能な限り、オブジェクトとデータ構造を不変にします。これは一般的に優れた方法であり、自動的にスレッド セーフになります。問題が解決しました。
  • オブジェクトを変更可能にする必要がある場合は、通常、わざわざスレッドセーフにしようとしないでください。これの理由は簡単です。変更可能な状態がある場合、ロック/コントロールを単一のクラスで安全に処理することはできません。すべてのメソッドを同期しても、スレッド セーフは保証されません。また、シングルスレッドのコンテキストでのみ使用されるオブジェクトに同期を追加すると、不要なオーバーヘッドが追加されます。したがって、必要なロックシステムが何であれ実装することは、呼び出し元/ユーザーに任せることもできます。
  • より高いレベルのパブリック API を提供する場合は、API スレッド セーフにするために必要なロックを実装します。より高いレベルの機能では、スレッド セーフのオーバーヘッドはごくわずかであり、ユーザーは間違いなく感謝するでしょう。ユーザーが回避する必要がある複雑な同時実行セマンティクスを持つ API は、適切な API ではありません。

このアプローチは、長い間私に役立ってきました。時折例外を作る必要があるかもしれませんが、平均して、それは始めるのに非常に良い場所です!

于 2012-03-08T06:43:34.363 に答える
1

コードのどのセグメントがマルチスレッド化され、どのセグメントがマルチスレッド化されないかを絶対に知っておく必要があります。

マルチスレッド化の領域を小さな制御可能なセクションに集中させることができなければ、成功することはありません。アプリのマルチスレッド部分は、慎重に検討し、完全に分析し、理解して、マルチスレッド環境に適応させる必要があります。

残りはそうではないため、スレッドセーフにするのは無駄です。

たとえば、swing GUI について、Sun は、そのいずれもマルチスレッド化しないと決定しました。

ああ、誰かがあなたのクラスを使用している場合、それがスレッド化されたセクションにある場合、それをスレッドセーフにするのは彼ら次第です。

Sun は当初、スレッドセーフなコレクション (のみ) を発表しました。問題は、スレッドセーフを非スレッドセーフにできないことです (パフォーマンス上の理由から)。そのため、スレッドセーフにするためのラッパーを備えた非スレッドセーフバージョンが登場しました。ほとんどの場合、ラッパーは不要です。ただし、スレッドを自分で作成する場合を除き、クラスをスレッドセーフにする必要はありませんが、javadocs でドキュメント化してください。

于 2008-10-24T17:00:48.903 に答える
0

「常に」はソフトウェア開発において非常に危険な言葉です...このような選択は「常に」状況に応じて行われます。

于 2008-10-24T17:13:27.927 に答える
0

Sun が Java API で行ったことを知りたい場合は、コレクション クラスを参照してください。多くの一般的なコレクション クラスはスレッド セーフではありませんが、対応するスレッド セーフなクラスがあります。Jon Skeet (コメントを参照) によると、Java クラスの多くは元々スレッドセーフでしたが、開発者にとって有益ではなかったため、一部のクラスには 2 つのバージョンがあり、1 つはスレッドセーフで、もう 1 つはスレッドセーフではありません。

私のアドバイスは、必要になるまでコードをスレッドセーフにしないことです。スレッドセーフにはオーバーヘッドが伴うからです。これは最適化と同じカテゴリに分類されると思います。必要になる前に実行しないでください。

于 2008-10-24T16:52:44.317 に答える
0

複数のスレッドから使​​用するクラスを個別に設計し、単一のスレッドのみから使用する他のクラスを文書化します。

シングルスレッドのものは、操作がはるかに簡単です。

マルチスレッド ロジックを分離すると、同期を正しく行うのに役立ちます。

于 2008-10-24T16:53:08.170 に答える
0

競合状態を回避するには、1 つのオブジェクトのみをロックします - 競合状態の説明を退屈に読むと、クロスロック (競合状態は誤称です - レースはそこで停止します) は常に 2 つ以上のスレッドがロックしようとする結果であることがわかります。 2 + オブジェクト。

すべてのメソッドを同期させ、テストを行います。実際に問題に対処しなければならない実際のアプリでは、同期のコストはわずかです。彼らがあなたに言わないのは、全体が16ビットのポインターテーブルでロックアウトするということです...その時点であなたは...

ハンバーガーのフリピンの履歴書を最新の状態に保つだけです。

于 2009-09-27T22:59:57.463 に答える