28

私の知る限り、サーブレット3の仕様では非同期処理機能が導入されています。特に、これは、同じスレッドが別の同時HTTPリクエストを処理するために再利用できることを意味します。これは、少なくとも以前にNIOで働いたことのある人にとっては革命的ではありません。

とにかく、これは別の重要なことにつながります。リクエストデータの一時ストレージとしての変数はありません。ThreadLocal同じスレッドが突然別のHTTPリクエストのキャリアスレッドになると、リクエストローカルデータが別のリクエストに公開されるためです。

それはすべて、記事を読んだことによる私の純粋な推測であり、サーブレット3の実装(Tomcat 7、GlassFish 3.0.Xなど)で遊ぶ時間がありません。

だから、質問:

  • ThreadLocalリクエストデータを保持するための便利なハックではなくなると思いますか?
  • 誰かがサーブレット3の実装のいずれかで遊んでThreadLocal、上記を証明するためにsを使用しようとしたことがありますか?
  • HTTPセッション内にデータを保存する以外に、アドバイスできる同様の簡単にアクセスできるハックはありますか?

編集:私を誤解しないでください。私は危険とThreadLocalハックであることを完全に理解しています。実際、私は常に同じような状況でそれを使用することはお勧めしません。ただし、信じられないかもしれませんが、スレッドコンテキストは、おそらく想像するよりもはるかに頻繁に使用されています。良い例はOpenSessionInViewFilter、JavadocによるとSpringのものです。

このフィルターは、トランザクションマネージャーによって自動検出される現在のスレッドを介してHibernateセッションを利用できるようにします。

これは厳密にThreadLocalはチェックされていませんが(ソースをチェックしていません)、すでに憂慮すべきことに聞こえます。私はもっ​​と似たシナリオを考えることができます、そしてウェブフレームワークの豊富さはこれをはるかに可能性の高いものにします。

簡単に言えば、多くの人々は、意識の有無にかかわらず、このハックの上に砂の城を建てました。したがって、スティーブンの答えは理解できますが、私が求めているものとはまったく異なります。誰かが実際に試みて失敗した行動を再現できたかどうかを確認したいので、この質問は同じ問題に巻き込まれた他の人への参照点として使用できます。

4

6 に答える 6

10

非同期処理は、明示的に要求しない限り、気にする必要はありません。

たとえば、サーブレットまたはリクエストのフィルタチェーン内のフィルタのいずれかが。でマークされていない場合、リクエストを非同期にすることはできません<async-supported>true</async-supported>。したがって、通常のリクエストには引き続き通常の方法を使用できます。

当然のことながら、実際に非同期処理が必要な場合は、適切な方法を使用する必要があります。基本的に、リクエストが非同期で処理される場合、その処理は部分に分割されます。これらのパーツはスレッドローカル状態を共有しませんが、パーツ間で状態を手動で管理する必要がありますが、各パーツ内でスレッドローカル状態を使用できます。

于 2011-02-21T11:30:44.333 に答える
6

(警告:サーブレット3の仕様を詳しく読んでいないので、仕様があなたが思っていることを正確に示しているとは言えません。私はそれが正しいと思っているだけです...)

ThreadLocalがリクエストデータを保持するための便利なハックではなくなると仮定するのは正しいですか?

ワーカースレッドがあるリクエストを終了して別のリクエストを開始すると、情報が漏洩するリスクが常に発生するため、使用ThreadLocalは常に不適切なアプローチでした。オブジェクトに属性としてものを保存することは、ServletRequest常により良い考えでした。

今、あなたはそれを「正しい」方法で行う別の理由を単に持っています。

サーブレット3の実装のいずれかで遊んで、ThreadLocalsを使用して上記を証明しようとした人はいますか?

それは正しいアプローチではありません。テストの特定の状況下での特定の実装の特定の動作についてのみ説明します。一般化することはできません。

正しいアプローチは、仕様で可能であると指定されている場合に発生することがあると想定し、それを考慮してWebアプリを設計することです。

(恐れることはありません!この場合、これはデフォルトでは発生しません。Webアプリは非同期処理機能を明示的に有効にする必要があります。コードにスレッドローカルが蔓延している場合は、これを行わないことをお勧めします...)

HTTPセッション内にデータを保存する以外に、アドバイスできる同様の簡単にアクセスできるハックはありますか。

いいえ。唯一の正しい答えは、リクエスト固有のデータをServletRequestまたはServletResponseオブジェクトに保存することです。特定のセッションで同時に複数のリクエストがアクティブになる可能性があるため、HTTPセッションに保存することさえ間違っている可能性があります。

于 2011-02-21T00:00:14.163 に答える
3

注:ハックが続きます。注意して使用するか、実際には使用しないでください。

コードが実行されているスレッドを理解し続ける限り、ThreadLocalを安全に使用できない理由はありません。

try {
    tl.set(value);
    doStuffUsingThreadLocal();
} finally {
    tl.remove();
}

コールスタックがランダムに切り替わるわけではありません。コールスタックの奥深くに設定してさらに使用したいThreadLocal値がある場合は、それもハックできます。

public class Nasty {

    static ThreadLocal<Set<ThreadLocal<?>>> cleanMe = 
        new ThreadLocal<Set<ThreadLocal<?>>>() {
            protected Set<ThreadLocal<?>> initialValue() {
                return new HashSet<ThreadLocal<?>>();
            }
        };

    static void register(ThreadLocal<?> toClean) {
       cleanMe.get().add(toClean);
    }

    static void cleanup()  {
        for(ThreadLocal<?> tl : toClean)
            tl.remove();
        toClean.clear();
    }
}

次に、ThreadLocalを設定時に登録し、finally句のどこかでクリーンアップします。これは、おそらくあなたがすべきではない恥ずべきことです。書いてすみませんが手遅れです:/

于 2011-03-02T04:02:53.063 に答える
1

なぜ人々が腐ったjavax.servletAPIを使用して実際にサーブレットを実装するのか疑問に思っています。私がやること:

  • HttpRequestHandlerリクエスト、レスポンスのプライベートフィールドと、例外をスローできるメソッドに加えて、パラメータや属性などを取得/設定するためのユーティリティメソッドを持つ基本クラスがありhandle()ます。サーブレットAPIの5〜10%以上が必要になることはめったにないので、これは思ったほどの作業ではありません。

  • サーブレットハンドラーで、このクラスのインスタンスを作成してから、サーブレットAPIを忘れます。

  • このハンドラークラスを拡張して、ジョブに必要なすべてのフィールドとデータを追加できます。巨大なパラメータリスト、スレッドローカルハッキング、同時実行性の心配はありません。

  • HttpRequestHandlerリクエストとレスポンスのモック実装を作成する単体テスト用のユーティリティクラスがあります。このように、コードをテストするためのサーブレット環境は必要ありません。

これにより、メソッドでDBセッションやその他のものを取得しinit()たり、サーブレットと実際のハンドラーの間にファクトリを挿入してより複雑なことを実行したりできるため、すべての問題が解決されます。

于 2011-03-02T09:56:48.043 に答える
1

あなたは超能力者です!(そのための+1)

私の目的は...これがサーブレット3.0コンテナで機能しなくなったことを証明することです

これがあなたが求めていた証拠です。

ちなみに、それはあなたがあなたの質問で述べたのとまったく同じOEMIVフィルターを使用していて、何を推測するか、それは非同期サーブレット処理を壊します!

編集:ここに別の証拠があります。

于 2013-05-01T20:29:06.287 に答える
0

1つの解決策は、ThreadLocalを使用せず、グローバルにするオブジェクトの静的配列を含むシングルトンを使用することです。このオブジェクトには、設定した「threadName」フィールドが含まれます。まず、現在のスレッドの名前(doGet、doPost)をランダムな一意の値(UUIDなど)に設定してから、シングルトンに格納するデータを含むオブジェクトの一部として格納します。次に、コードの一部がデータにアクセスする必要があるときはいつでも、配列を調べて、現在実行中のthreadNameを持つオブジェクトをチェックし、オブジェクトを取得します。httpリクエストが完了したときに配列からオブジェクトを削除するには、クリーンアップコードを追加する必要があります。

于 2018-05-02T18:48:44.420 に答える