Timからの受け入れられた回答と、pacmanからのフォローアップの質問に追加するには、AsyncResponseまたは同様の機能をNIOコネクタと一緒に使用する場合は注意が必要です。ティムが何を意味するのかわかりません。「[非同期]サーブレットが1つのリクエストに対して複数回呼び出される可能性があります」...しかし、「リクエスト」が単一の「GET」、「PUT」、「POST」を指す場合、または「DELETE」してからAFAIKを実行すると、サーブレット内の対応するリソースメソッドが1回呼び出されます。
ThreadLocalsと非同期リソースで発生する可能性のある問題の1つは、非同期リソースの処理スレッドがNIOイベントループスレッドからのThreadLocal変数のコピーを必要とするかどうかです。つまり、NIOイベントループスレッドはリクエストを受け入れてから、非同期リソースに制御を渡します...次に、そのリソースは子スレッドに制御を渡します...次に、NIOイベントループスレッドは別のリクエストを自由に処理できます... NIOイベントループスレッド内のThreadLocal変数は、後続のリクエストによって踏みにじられる可能性があります。
新しいリクエストごとにThreadLocalに格納されているオブジェクトの新しいインスタンスを作成することも可能であることに注意してください...その場合、新しいリクエストはそれぞれ、以前のリクエスト中に同じThreadLocalに格納されていた古いインスタンスを踏みにじることはありません。 。しかし、どのケースを扱っているかを確認する必要があります...いくつかの例を見てみましょう。
元の質問はSpringに言及しているので、良い例はThreadLocalを持つRequestContextHolderです。NIOイベントループスレッドの名前が「http-nio-8080-exec-1」で、AsyncResponseリソースに制御を渡し、AsyncResponseリソースがExecutorを介して新しいスレッド(「pool-2-thread-3」)を起動するとします。 。新しいスレッドには、AsyncResponse.resume()を介して返される回答を取得するためにRequestAttributesから何かを必要とするコードが含まれています。スレッド「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コードはこのように機能するため、注意してください)。安全です)。
反対の例は、マップのlog4jMDCThreadLocalコピーです。この場合、新しいリクエストはそれぞれ同じマップを再利用します...したがって、マップの参照をNIOイベントループスレッドからAsyncResponseスレッドに渡すことは安全ではありません...マップのコピーを作成して渡す必要があります。これを行う方法の例については、MDCAwareThreadPoolExectutorを参照してください。
基本的に、NIOイベントループスレッドからAsyncResponseスレッドに渡す必要がある各ThreadLocal変数をチェックする必要があります...そして、元のオブジェクトへの参照を渡すだけで安全かどうか、または必要があるかどうかを確認します。ワーカースレッドの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());
});