ThreadLocalの浅い分析

4344 ワード

ThreadLocalって何?


JDK 1.2のバージョンではjavaが提供されています.lang.ThreadLocal,ThreadLocalはマルチスレッドプログラムの同時問題を解決するために新しい考え方を提供した.このツールクラスを使用すると、美しいマルチスレッドプログラムを簡潔に作成できます.java.lang.ThreadLocalはスレッドローカル(thread-local)変数を提供します.これらの変数は、ある変数(getメソッドまたはsetメソッドによって)にアクセスする各スレッドに独自のローカル変数があり、変数の初期化コピーとは独立しているため、通常の対応物とは異なります.ThreadLocalインスタンスは、通常、あるスレッド(例えば、ユーザIDまたはトランザクションID)にステータスを関連付けるクラスのprivate staticフィールドです.各スレッドは、スレッドがアクティブであり、ThreadLocalインスタンスがアクセス可能である限り、スレッドのローカル変数のコピーに対する暗黙的な参照を保持します.スレッドが消えた後、そのスレッドローカルインスタンスのすべてのコピーはGCによって回収されます(これらのコピーに対する他の参照がない限り).

ThreadLocalインタフェースメソッド

  • ThreadLocalは、4つの方法を提供しています.
  • void set(T value)現在のローカル変数の値
  • を設定する.
       public void set(T value) {
            Thread t = Thread.currentThread();
            ThreadLocalMap map = getMap(t);
            if (map != null)
                map.set(this, value);
            else
                createMap(t, value);
        }
    
  • product T initialValue()はスレッドのローカル変数の初期値を返します.この方法はprotectedの方法で、方法の内部には具体的な実装コードがなく、明らかにサブクラスを上書きするために設計されています.この方法は,スレッドがget()またはset(Object)を1回目に呼び出したときに実行され,1回のみ実行される遅延呼び出し方法である.ただし、get()を呼び出した後にremove()を呼び出すと、このメソッドが再び呼び出される可能性があります.
  •  protected T initialValue() {
           return null;
       }
    
  • public T get()このメソッドは、現在のスレッドに対応するスレッドローカル変数を返します.
  •        Thread t = Thread.currentThread();
           ThreadLocalMap map = getMap(t);
           if (map != null) {
               ThreadLocalMap.Entry e = map.getEntry(this);
               if (e != null)
                   return (T)e.value;
           }
           return setInitialValue();
       }
    
  • public void remove()現在のスレッドのローカル変数の値を削除します.メモリの使用量を減らすためです.この方法はJDK 5.0で追加された方法です.実際にスレッド終了後に対応するスレッド変数は自動的にGCされ、このメソッドをアクティブに呼び出す目的はできるだけ早く回収し、メモリを解放することである.
  •   public void remove() {
             ThreadLocalMap m = getMap(Thread.currentThread());
             if (m != null)
                 m.remove(this);
         }
    

    ソースコードの浅い分析


    JDKのソースコードを分析するのは理解しにくいかもしれませんが、ThreadLocalクラスを簡略化することができます.簡略化されたコードは以下の通りです.
    class MyThreadLocal{
    private Map map = new HashMap();
         protected T initialValue() {
            return null;
        }
      
      public void set(T value) {
            map.put(Thread.currentThread(), value);
        }
        
        public void remove() {
            map.remove(Thread.currentThread());
        }
        
        public T get() {
            return map.get(Thread.currentThread());
        }
    }
    

    実際にはThreadLocal内部では1つのMapを維持してデータを保存するものであり、使用時に取得する値はvalue値であるが、それぞれのvalueに対応するkeyは現在のスレッドであるため、変数を各スレッドに1部コピーすることに相当し、各スレッドはデータの動作に相互に影響しない.

    実用的なケース


    私たちはデータベースを操作するとき、データセキュリティのためにトランザクション操作を加えなければならないことがありますが、一般的なトランザクション管理はService層にあり、データ操作はDAO層にあります.一般的な方法は、Service層に接続を確立してDAO層にパラメータを渡すことです.これはプログラムの侵入性を増加させるに違いありません.Springのトランザクション管理の下部ではThreadLocalを使用してこのような状況を解決しています.また、ThreadLocalを使用してトランザクション操作ツールクラスを記述することもシミュレーションできます.
    public class JdbcUtils {
        private static DataSource dataSource = new ComboPooledDataSource();
        private static ThreadLocal tl  = new ThreadLocal();
      // 
        public static DataSource getDataSource() {
            return dataSource;
        }
      // 
        public static Connection getConnection() throws SQLException {
            Connection con = tl.get(); 
            if(con == null) {
                return dataSource.getConnection();
            }
            return con;
        }
        // 
        public static void beginTranscation() throws SQLException {
            Connection con = tl.get(); 
            if(con != null ) {
                throw new SQLException(" , , !");
            }
            con = dataSource.getConnection(); 
            con.setAutoCommit(false); 
            tl.set(con); 
        }
        // 
        public static void commitTransaction() throws SQLException {
            Connection con = tl.get(); 
            if(con == null ) {
                throw new SQLException(" , !");
            }
            con.commit(); 
            con.close(); 
            tl.remove(); 
        }
        //  
        public static void rollbackTransaction() throws SQLException {
            Connection con = tl.get();
            if(con == null) {
                throw new SQLException(" , !");
            }
            con.rollback();
            con.close();
            tl.remove();
        }
    }