PammanからのTimとフォローアップの質問から受け入れられた回答に追加するには、AsyncResponseまたは同様の機能をNIOコネクタとともに使用するときに注意する必要があります。私は、Timが何を意味するのか分かりません。 "あなたの[async]サーブレットは、単一の要求に対して複数回呼び出されるかもしれません" ...しかし、 "要求"が単一の "GET"、 "PUT"、 "POST" 、 "DELETE"、そしてAFAIKを実行すると、サーブレット内の対応するリソースメソッドを1回呼び出すことになります。
ThreadLocalsとAsyncリソースで実行できる問題の1つは、非同期リソース内の処理スレッドがNIOイベントループThreadのThreadLocal変数のコピーを必要とする場合です。言い換えれば、NIOイベントループのスレッドは要求を受け取り、次に非同期リソースに制御を渡します...そのリソースは子スレッドに制御を渡します。そして、NIOイベントループスレッドは別の要求を自由に処理できます... so NIOイベントループThread 内の任意のThreadLocal変数は、後続の要求によってストンプされる可能性があります。
新しいリクエストごとに、ThreadLocalに格納されたObjectの新しいインスタンスを作成することもできます。この場合、新しいリクエストごとに、以前のリクエスト中に同じThreadLocalに格納されていた古いインスタンスが詰まることはありません。あなたが扱っているケースを確認する必要があります...いくつかの例を見てみましょう。
元の質問はSpringを指すので、良い例はThreadLocalを持つRequestContextHolderです。 NIOイベントループスレッドの名前が「http-nio-8080-exec-1」で、制御がAsyncResponseリソースに渡され、Executorを介して新しいスレッド(「pool-2-thread-3」という名前の) 。新しいスレッドには、RequestAttributesから何かを必要とするコードがあり、AsyncResponse.resume()を介して返答を返すようになっています。スレッド "pool-2-thread-3"で実行されているコードは "http-nio-8080-exec-1"からRequestAttributesにアクセスする必要があるので、2つのことを確認する必要があります。
1)あなたのリソースは "http-nio-8080-exec-1"からRequestAttributesへの参照を取得し、それを "pool-2-thread-3"に渡します。
2) "http-nio-8080-exec-1"が新しいリクエストを受け入れると、RequestAttributesの新しいコピーを作成し、それを新しいリクエストのRequestContextHolderのThreadLocalコピーにセットします(Springコードはこのように動作します。安全です)。
逆の例は、マップのlog4j MDC ThreadLocalコピーです。この場合、新しいリクエストごとに同じMapが再利用されるため、NIOイベントループのスレッドからAsyncResponseスレッドへマップの参照を渡すことは安全ではありません。マップのコピーを作成して渡す必要があります。 MDCAwareThreadPoolExectutor を参照してください。
基本的には、NIOイベントループのスレッドからAsyncResponseスレッドに渡す必要がある各ThreadLocal変数をチェックして、元のObjectへの参照を渡すだけで安全かどうかを確認する必要があります。ワーカースレッドのThreadLocal変数にコピーを設定する前に、オブジェクトのコピーを作成します。
ところで、上記の2つの例を組み合わせたコードがあります:
public class RequestContextAwareThreadPoolExecutor extends MDCAwareThreadPoolExecutor {
/* ... constructors left out ... */
@Override
public void execute(Runnable runnable) {
super.execute(wrap(runnable, RequestContextHolder.currentRequestAttributes()));
}
Runnable wrap(final Runnable runnable, final RequestAttributes requestAttributes) {
return() -> {
RequestContextHolder.setRequestAttributes(requestAttributes);
try {
runnable.run();
} finally {
RequestContextHolder.resetRequestAttributes();
}
};
}
}
AsyncResponseリソースから、次のような呼び出しを行います。
executor.execute(() -> {
//veryLongOperation() needs to access the RequestAttributes and the MDC
asyncResponse.resume(veryLongOperation());
});