ThreadLocalスレッド多重化の問題

2980 ワード

JavaのThreadLocalクラスでは、同じスレッドでしか読み書きできない変数を作成できます.したがって、あるコードにThreadLocal変数の参照が含まれている場合、2つのスレッドが同時にこのコードを実行しても、相手のThreadLocal変数にアクセスできません.
私たちの日常的なWeb開発では、1つのパラメータを最内層に渡す必要があることは避けられませんが、中間層はこのパラメータを使用する必要がない可能性があります.そのため、各方法でこのような共通のパラメータを伝える必要はありません.JavaのWebプロジェクトの大部分はTomcatに基づいており、アクセスするたびに新しいスレッドであることを連想させ、各スレッドはThreadLocalを独占し、リクエストを受信するときに特定のコンテンツをセットし、必要に応じてgetという値を与える.例えば、ログイン情報はこのように格納することができる.
ThreadLocal<Map> userInfo = new ThreadLocal<Map>();

しかし、userInfoを取得する過程で、それがない場合もあり、他のユーザーのログイン情報もあることがわかります.場合によってはなく、ページが初めてこのページにジャンプした場合、クッキーにログイン情報がないため、ブロッカーもログイン情報を取得できないため、値を空にします.
他のユーザの登録情報があるのは,登録情報に値を付与する際に,オブジェクトに値があるか,値がある場合は元の値を上書きしないか,あるいはその値を付与するブロッキングを行っていないため,つまりメモリリークの問題が発生しているためである.
各リクエストにはThreadがあり、各ThreadにはThreadLocalMap変数があり、そのThreadに属するすべてのThreadLocal変数を格納します.
/* ThreadLocal values pertaining to this thread. This map is maintained
     * by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;

Mapのkeyはthreadlocalの例である.
static class ThreadLocalMap {

    /**
     * The entries in this hash map extend WeakReference, using
     * its main ref field as the key (which is always a
     * ThreadLocal object).  Note that null keys (i.e. entry.get()
     * == null) mean that the key is no longer referenced, so the
     * entry can be expunged from table.  Such entries are referred to
     * as "stale entries" in the code that follows.
     */
    static class Entry extends WeakReference<ThreadLocal> {
        /** The value associated with this ThreadLocal. */
        Object value;

        Entry(ThreadLocal k, Object v) {
            super(k);
            value = v;
        }
    }
}

このMapは確かに弱い参照を使用していますが、弱い参照はkey.の各keyに対して弱い参照がthreadlocalを指しています.threadlocalインスタンスをnullに設定した後、threadlocalインスタンスを指す強い参照はありません.そのため、threadlocalはgcによって回収されます.しかし、私たちのvalueは回収できません.current threadから接続された強い参照が存在するため、現在のthreadが終了した後だけ、current threadはスタックに存在せず、強い参照が切断され、Current Thread,Map,valueはすべてGCで回収される.このスレッドオブジェクトがgcによって回収される限りメモリリークは発生しないが,スレッドプールを使用する場合,リクエスト終了後にスレッドは破棄されず,再多重化されるためメモリリークが発生する.したがって、ThreadLocalのビジネスサイクル処理が完了したときに表示される呼び出しremove()メソッドは、「スレッドローカル変数」の値をクリアするか、ビジネスサイクルの開始時に値を再割り当てすることをお勧めします.