I. 概要
スレッドローカルとは何ですか?実は、ThreadLocal はスレッドのローカル実装版ではなく、Thread ではなく、スレッドローカル変数 (スレッドローカル変数) です。おそらく、ThreadLocalVar という名前を付ける方が適切でしょう。スレッド ローカル変数 (ThreadLocal) の実際の機能は非常に単純です。変数を使用する各スレッドに変数値のコピーを提供することです。これは、各スレッドが独立して独自の変数を変更できるようにする、Java の特別なスレッド バインディング メカニズムです。他のスレッドのコピーと競合せずにコピーします。
スレッドの観点から見ると、各スレッドは、スレッドがアクティブで ThreadLocal インスタンスにアクセスできる限り、スレッド ローカル変数のコピーへの暗黙的な参照を維持します。スレッドが消滅すると、そのスレッド ローカル インスタンスのコピーはすべてガベージになります。コレクション (これらのコピーへの他の参照がない限り)。
ThreadLocal を介してアクセスされるデータは常に現在のスレッドに関連しています。言い換えれば、JVM はプライベート ローカル インスタンスのアクセス スペースを実行中の各スレッドにバインドし、マルチスレッド環境でよく発生する同時アクセスの問題に対するソリューションを提供します。 。
ThreadLocal は各スレッドの変数のコピーをどのように維持するのでしょうか?実際、実装のアイデアは非常に単純で、ThreadLocal クラスに Map があり、各スレッドの変数のコピーを保存するために使用されます。
要約すると、マルチスレッドのリソース共有の問題に対して、同期機構は「時間を空間に交換する」という手法を採用しているのに対し、ThreadLocal は「空間を時間に交換する」という手法を採用しています。前者は、アクセスをキューに入れるさまざまなスレッドに変数のコピーのみを提供しますが、後者は各スレッドに変数のコピーを提供するため、相互に影響を与えることなく同時にアクセスできます。
2. API の説明
ThreadLocal()
スレッドローカル変数を作成します。
T get()
このスレッド ローカル変数の現在のスレッドのコピーの値を返します。この変数は、スレッドがこのメソッドを初めて呼び出す場合に作成され、初期化されます。
protected T 初期値()
このスレッドローカル変数の現在のスレッドの初期値を返します。このメソッドは、各スレッド ローカル変数を取得するために、スレッド アクセスごとに最大 1 回呼び出されます。つまり、スレッドが get() メソッドを使用して最初に変数にアクセスしたときです。スレッドが get メソッドの前に set(T) メソッドを呼び出した場合、initialValue メソッドはスレッド内で再度呼び出されません。
この実装が単純に null を返す場合、プログラマがスレッドローカル変数を null 以外の値に初期化したい場合は、プログラマは ThreadLocal をサブクラス化し、このメソッドをオーバーライドする必要があります。通常、匿名の内部クラスが使用されます。 InitialValue の一般的な実装では、適切なコンストラクターを呼び出し、新しく構築されたオブジェクトを返します。
void 削除()
このスレッドローカル変数の値を削除します。これは、スレッドローカル変数のストレージ要件を軽減するのに役立つ場合があります。このスレッドローカル変数が再度アクセスされると、デフォルトで初期値が設定されます。
void set(T 値)
このスレッドローカル変数の現在のスレッドのコピーの値を、指定された値に設定します。多くのアプリケーションはこの機能を必要とせず、initialValue() メソッドのみに依存してスレッドローカル変数の値を設定します。
プログラムでは、通常、initialValue メソッドは特定の初期値を与えるためにオーバーライドされます。
3. 代表的な例
1. Hiberante のセッション ツール クラス HibernateUtil
このクラスは Hibernate の公式ドキュメントの HibernateUtil クラスであり、セッション管理に使用されます。
パブリック クラス HibernateUtil {
プライベート静的ログ ログ = LogFactory.getLog(HibernateUtil.class);
private static Final SessionFactory sessionFactory; //SessionFactoryを定義します
静的 {
試す {###### //デフォルト設定ファイル hibernate.cfg.xml を通じて SessionFactory
を作成します sessionFactory = new Configuration().configure().buildSessionFactory();
} catch (スロー可能な例) {
log.error("SessionFactory の初期化に失敗しました!"、例);
新しい ExceptionInInitializerError(ex) をスローします;
}
}
//スレッドローカル変数セッションを作成して Hibernate のセッションを保存します
public static 最終 ThreadLocal セッション = new ThreadLocal();
/**
* 現在のスレッドでセッション
を取得します * @return セッション
* @throws HibernateException
*/
public static Session currentSession() が HibernateException をスローする {
セッション s = (セッション) session.get();
// セッションがまだ開いていない場合は、新しいセッションを開きます
if (s == null) {
s = sessionFactory.openSession();
session.set(s); //新しく開いたセッションをスレッドローカル変数に保存します
}
戻り値;###### }
public static void closeSession() は HibernateException をスローします {
//スレッドローカル変数を取得し、セッションタイプ
にキャストします セッション s = (セッション) session.get();
session.set(null);
if (s != null)
s.close();
}
}
このクラスでは、ThreadLocalのinitialValue()メソッドがオーバーライドされていないため、初めてスレッドローカル変数sessionが作成され、その初期値はnullとなります。スレッドのローカル変数も null です。したがって、セッションが判定されます。null の場合は、新しいセッションが開かれ、スレッド ローカル変数 session に保存されます。このステップは非常に重要です。これも、「public static Final ThreadLocal session = new ThreadLocal()」によって作成されるオブジェクトです。 " セッションを Hibernate Session オブジェクトに強制できる理由。
2. 別の例
Bean を作成し、さまざまなスレッド オブジェクトを通じて Bean プロパティを設定して、各スレッド Bean オブジェクトの独立性を確保します。
/**
* IntelliJ IDEA によって作成されました。
* ユーザー: leizhimin
* 日付: 2007-11-23
* 時間: 10:45:02
*学生
*/
公開クラス 学生 {
private int age = 0; //年齢
public int getAge() {
この年齢を返します;
}
public void setAge(int age) {
this.age = 年齢;
}
}
/**
* IntelliJ IDEA によって作成されました。
* ユーザー: leizhimin
* 日付: 2007-11-23
* 時間: 10:53:33
* マルチスレッドでのテストプログラム
*/
パブリック クラス ThreadLocalDemo は Runnable {
を実装します // スレッド ローカル変数 StudentLocal を作成します。これは後で Student オブジェクトを保存するために見つかります
private Final static ThreadLocal StudentLocal = new ThreadLocal();
public static void main(String[] agrs) {
ThreadLocalDemo td = 新しい ThreadLocalDemo();
スレッド t1 = 新しいスレッド(td, "a");
スレッド t2 = 新しいスレッド(td, "b");
t1.start();
t2.start();
}
public void run() {
accessStudent();
}
/**
* テスト用のサンプル ビジネスメソッド
*/
public void accessStudent() {
//現在のスレッドの名前を取得します
文字列 currentThreadName = Thread.currentThread().getName();
System.out.println(currentThreadName " が実行中です!");
//乱数を生成して出力します
ランダム ランダム = new Random();
int age = ランダム.nextInt(100);
System.out.println("スレッド " currentThreadName " set age to:" age);
//Student オブジェクトを取得し、ランダムな年齢をオブジェクト属性に挿入します
学生 Student = getStudent();
Student.setAge(年齢);
System.out.println("スレッド " currentThreadName " の最初の読み取り年齢は次のとおりです:"student.getAge());
試す {###### Thread.sleep(500);
}
catch (InterruptedException ex) {
ex.printStackTrace();
}
System.out.println("thread " currentThreadName " 2 番目の読み取り年齢は:"student.getAge());
}
protected Student getStudent() {
//ローカルスレッド変数を取得し、Student タイプ
にキャストします。 学生 Student = (学生) StudentLocal.get();
//スレッドがこのメソッドを初めて実行するとき、studentLocal.get() は null
でなければなりません if (学生 == null) {
// Student オブジェクトを作成し、ローカル スレッド変数 StudentLocal
に保存します。 学生 = 新しい学生();
StudentLocal.set(student);
}
帰国生;
}
}
操作結果:
実行中です!
設定年齢を 76
に設定します。 b は実行中です!
スレッド b は年齢を 27
に設定します スレッドの最初の読み取り年齢は:76
スレッド b の最初の読み取り年齢は:27
スレッドの 2 番目の読み取り期間は:76
スレッド b の 2 番目の読み取り期間は:27
異なる時間に経過した 2 つのスレッドによって出力された値がまったく同じであることがわかります。このプログラムは、ThreadLocal を使用して、データのセキュリティを考慮しながらマルチスレッドの同時実行を実現します。
4. まとめ
ThreadLocal は主に、マルチスレッドでの同時実行によるデータの不整合の問題を解決するために使用されます。 ThreadLocal は、各スレッドで同時にアクセスされるデータのコピーを提供し、そのコピーにアクセスすることでビジネスを実行することで、メモリを消費し、スレッドの同期によるパフォーマンスの消費を大幅に削減し、スレッドの同時実行制御の複雑さも軽減します。
ThreadLocal はアトミック タイプを使用できず、オブジェクト タイプのみを使用します。 ThreadLocal の使用は、同期よりもはるかに簡単です。
ThreadLocal と Synchronized は両方とも、マルチスレッドの同時アクセスを解決するために使用されます。ただし、ThreadLocal と synchronized の間には本質的な違いがあります。 Synchronized はロック メカニズムを使用するため、変数またはコード ブロックには一度に 1 つのスレッドのみがアクセスできます。 ThreadLocal は各スレッドに変数のコピーを提供するため、各スレッドが特定の時点で同じオブジェクトにアクセスすることがなくなり、複数のスレッドによるデータの共有が分離されます。同期はその逆で、複数のスレッド間で通信するときにデータを共有するために使用されます。
Synchronized はスレッド間のデータ共有に使用され、ThreadLocal はスレッド間のデータ分離に使用されます。
もちろん、ThreadLocal は synchronized を置き換えることはできず、別の問題ドメインに対処します。 Synchronized は同期メカニズムの実装に使用され、ThreadLocal よりも複雑です。
5. ThreadLocal
を使用するための一般的な手順 1. マルチスレッド クラス (ThreadDemo クラスなど) で、ThreadLocal オブジェクト threadXxx を作成し、スレッド間で分離する必要があるオブジェクト xxx を保存します。
2. ThreadDemo クラスで、分離アクセスするデータを取得するメソッド getXxx() を作成し、メソッド内で ThreadLocal オブジェクトが null の場合、分離アクセス タイプのオブジェクトを new() して強制的に取得する必要があると判断します。適用されるタイプ。
3. ThreadDemo クラスの run() メソッドでは、getXxx() メソッドで操作対象のデータを取得することで、各スレッドがデータ オブジェクトに対応し、そのオブジェクトが随時操作されるようになります。
以上がjava.lang.ThreadLocal クラスの使用方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。