web-dev-qa-db-ja.com

ThreadLocalの目的は?

与えられたThreadLocalの目的 ここ は、変数がThreadLocal変数を含むオブジェクトにアクセスするすべてのスレッドに対してローカルであることを示しています。 ThreadLocal変数をクラスのメンバーとして持ち、それをスレッド自体に対してローカル変数にするのではなく、スレッドに対してローカルにするという点で、どのような違いがありますか?

33
Ajay

スレッドは実行の単位であるため、複数のスレッドが同じコードを同時に実行できます。複数のスレッドがオブジェクト/インスタンスで同時に実行される場合、それらはインスタンス変数を共有します。各スレッドには独自のローカル変数がありますが、パラメーターを渡さずにオブジェクト間でこれらを共有することは困難です。

例として最もよく説明されています。ログインしたユーザーを取得してコードを実行するサーブレットがあるとします。

doGet(HttpServletRequest req, HttpServletResponse resp) {
  User user = getLoggedInUser(req);
  doSomething()
  doSomethingElse()
  renderResponse(resp)
}

DoSomething()メソッドがユーザーオブジェクトにアクセスする必要がある場合はどうなりますか?各スレッドが同じユーザーオブジェクトを使用するため、ユーザーオブジェクトをインスタンスまたは静的変数にすることはできません。ユーザーオブジェクトをパラメーターとして渡すこともできますが、これはすぐに面倒になり、すべてのメソッド呼び出しにユーザーオブジェクトがリークします。

doGet(HttpServletRequest req, HttpServletResponse resp) {
  User user = getLoggedInUser(req);
  doSomething(user)
  doSomethingElse(user)
  renderResponse(resp,user)
}

より洗練された解決策は、ユーザーオブジェクトをThreadLocalに配置することです。

doGet(HttpServletRequest req, HttpServletResponse resp) {
  User user = getLoggedInUser(req);
  StaticClass.getThreadLocal().set(user)
  try {
    doSomething()
    doSomethingElse()
    renderResponse(resp)
  }
  finally {
    StaticClass.getThreadLocal().remove()
  }
}

これで、いつでもユーザーオブジェクトを必要とするコードは、それらの厄介な追加パラメーターに頼る必要なしに、ローカルスレッドから抽出することでそれを取得できます。

User user = StaticClass.getThreadLocal().get()

このアプローチを使用する場合は、finallyブロックでオブジェクトを再度削除するように注意してください。そうしないと、スレッドプールを使用する環境(Tomcatアプリサーバーなど)でユーザーオブジェクトがハングする可能性があります。

編集:静的クラスのコード

class StaticClass {
  static private ThreadLocal<User> threadLocal = new ThreadLocal<>();

  static ThreadLocal<User> getThreadLocal() {
    return threadLocal;
  }
}
80
leonm

スレッドを拡張するクラスのインスタンスは not 実際のJavaスレッドと同じものではないことを理解する必要があります(コードを実行して実行する「実行ポインタ」として想像してください)。

このようなクラスのインスタンス represent Javaスレッドを操作(割り込みなど)できますが、それ以外は通常のオブジェクトとそのメンバーですオブジェクトへの参照を取得できるすべてのスレッドからアクセスできます( ハードではありません )。

もちろん、メンバーをプライベートに保ち、メンバーがrun()またはそのメンバーから呼び出されるメソッド(パブリックメソッドは他のスレッドからも呼び出すことができます)によってのみ使用されるようにすることはできますが、これはエラーが発生しやすいですまた、データをすべてThreadサブクラスに保持したくない(実際にはThreadをサブクラス化するのではなく、代わりにRunnableを使用する)複雑なシステムでは現実的ではありません。

ThreadLocalは、多大な労力や設計上の妥協を必要とせずに、他のスレッドから同時にアクセスできないスレッドごとのデータを取得するためのシンプルで柔軟な方法です。

13

Threadオブジェクトは内部データメンバーを持つことができますが、これらはThreadオブジェクトへの参照を持っている(または取得できる)誰でもアクセスできます。 ThreadLocalは、それにアクセスする各スレッドにのみ意図的に関連付けられています。利点は、(ThreadLocalのコンテキスト内で)並行性の問題がないことです。スレッドの内部データメンバーには、共有状態と同じ同時実行の問題があります。

結果を特定のスレッドに関連付けるという考え方を説明しましょう。 ThreadLocalの本質は次のようなものです。

public class MyLocal<T> {
  private final Map<Thread, T> values = new HashMap<Thread, T>();

  public T get() {
    return values.get(Thread.currentThread());
  }

  public void set(T t) {
    values.put(Thread.currentThread(), t);
  }
}

今ではそれ以上のものがありますが、ご覧のとおり、返される値は現在のスレッドによって決定されます。そのため、各スレッドに対してlocalです。

8
cletus

ThreadLocalは、Webアプリケーションで非常に役立ちます。典型的なパターンは、Web要求の処理の開始時のどこか(通常はサーブレットフィルター内)で、状態がThreadLocal変数に格納されるというものです。リクエストのすべての処理は1つのスレッドで行われるため、リクエストに参加しているすべてのコンポーネントがこの変数にアクセスできます。

6
Kees de Kooter

この問題領域に関するウィキペディア エントリ があります。私たちの環境では、通常、要求するものをローカルに保つために使用されます。サーバー側では、リクエストは主に単一のスレッドによって処理されます。物事をローカルに保つために、データを配置します。スレッドローカル変数内のセッションデータ。このデータは他のリクエスト(スレッド)からは見えないため、他のリクエストと同期する必要はありません。

そして忘れないでください、Java APIコンストラクト、つまりnotスレッドセーフ、例えば DateFormat 。DateFormatの静的インスタンスはサーバー側では機能しません。

実際、データの独自のプライベートコピーを使用する場合は、ロックやモニターを使用するよりも、マルチスレッドプログラミングを処理する方が簡単です。

2
dz.

ThreadLocalsの利点は、プレーンなVanilla Threads ...またはThreadのサブクラスで実行されるメソッドで使用できることです。

対照的に、スレッドローカルをThreadのカスタムサブクラスのメンバーとして実装する必要がある場合、実行できないことがたくさんあります。たとえば、既存のVanilla Threadインスタンスでメソッドを実行する必要がある場合、アプリケーションは問題になります。つまり、アプリケーションの作成者が作成しておらず、変更できないライブラリコードによって作成されたインスタンス。

1
Stephen C