マルチスレッド環境での私の主な経験はScalaを使用していたため、複数のスレッドによって同時に更新されるオブジェクトがある場合は、常にAkkaを使用しました。 Java環境で作業しています。同時に更新されたオブジェクトがある単純なケースにすべてのAkkaの依存関係とフューチャーの複雑さをもたらしたくありません。アクター以外に受け入れられる代替策は何ですか?スレッド間で更新可能な状態を共有するためのモデル?
一般的に使用される3つのパラダイムは、アクター(Akkaなど)、ソフトウェアトランザクションメモリ(Clojureなど)、または従来の手動ロックラングリングです。彼らはすべて独自の課題を抱えています。
従来のロックベースのプログラミングでは、同時アクセスによる破損からの保護が必要な場所を検出し、ロックによってそれらを保護し、ソリューションが正しく、デッドロックがなく、効率的であることを期待しています。 (Venkat Subramaniamはこれを「同期して苦しむ」と呼んでいます)。このアプローチには多くの問題があります。多くの言語では、正確さを保証するのにほとんど何の役にも立ちません。スレッドセーフはコンポジションの下では保持されないため、開発はスケーリングしません(コードについて何かを変更するたびに、ソリューションが正しいことを再証明する必要があります)。このモデルは他のすべてのモデルの基礎となりますが、それは事実上、並行性のアセンブラープログラミングであり、操作するのと同じくらい不愉快です。
あなたがすでに知っている俳優。ここでの基本的な概念は、非同期メッセージと、可変状態のビットに対するアクターの排他的責任です。合言葉は分離された可変性です。これに関する問題の1つは、アクターの背後に隠されているリソースの1つがボトルネックである場合、非常に非効率になる可能性があることです。また、アクターを不適切に使用すると、デッドロックが発生する可能性があります。
ソフトウェアトランザクショナルメモリは共有不変性として最適に表示されます。通常の説明は、すべてのスレッドがやりたいことを実行するだけであり、実際に競合が発生したことを検出すると、モニターは1つのスレッドのシーケンス全体を元に戻します。ニースのメタファーはダウンタイムなしでWebサービスを更新します。2番目の同じサーバーをセットアップし、それをアップグレードします。それが機能した場合は、スイッチをスローしてすべてのトラフィックを新しいアドレスにリダイレクトし、古いアドレスをシャットダウンします。予期しないことが起こった場合(たとえば、アップグレードが完了する前に要件が変更された場合)、新しいサーバーを破棄してやり直すだけです。
STMの欠点は、不変性を維持するために必要なすべてのコピーが高額になり、プログラミングを理解するのが非常に難しいことです。 STMは基本的に復讐を伴うロックフリープログラミングであり、ロックフリープログラミングは多くの人々の頭にそのまま残っています。多くの場合、それはあなたがずっと前にすでに解決したと思った問題のための新しいアルゴリズムを見つけることを必要とします。また、競合とロールバックが比較的まれである場合にのみ、効率的です。
どのモデルを選択するかは、多くの要因によって異なります。私の個人的な印象は、ツールのサポートは実際には基礎となるパラダイムよりも重要であるということです。言語のサポートがよく知られていないパラダイムを使ってプログラミングする方が、隣り合わせのパラダイムを使ってプログラミングするよりもはるかに簡単です。