多線程學習 ThreadLocal的使用。
ThreadLocal ,即線程變量,是一個以ThreadLocal對象為鍵,任意對象為值得存儲接口。這個接口被附帶在線程上,也就是說一個線程可以根據一個ThreadLocal對象查詢到綁定在這個線程上的值。
可以通過set(T)方法來設置一個值,在當前線程下,在通過get()方法獲取到原先設置的值。
上面的文字是不是有點晦澀?來,學習一下明白的。
變量值得共享可以使用 public static 變量的形式,所有的線程都使用同一個 public static 變量。如果想實現每一個線程都有自己的共享變量該如何解決那?
jdk提供了ThreadLocal正是為了解決這樣的問題。
類ThreadLocal 主要解決的就是每個線程綁定自己的值,可以將ThreadLocal類比喻成全局存放數據的盒子,盒子中可以存儲每個線程的私有數據。
下面實驗:
1 創建ThreadLocal對象,用來存儲每個線程的私有值。
public class Tools { public static ThreadLocal t=new ThreadLocal(); }
2 創建兩個線程A,B.
public class ThreadA extends Thread { @Override public void run() { super.run(); try { for(int i=0;i<100;i++){ Tools.t.set("ThreadA "+(i+1)); System.out.println("ThreadA get Value " + Tools.t.get()); Thread.sleep(200); } } catch (Exception e) { e.printStackTrace(); } } }
public class ThreadB extends Thread{ @Override public void run() { super.run(); try { for(int i=0;i<100;i++){ Tools.t.set("ThreadB "+(i+1)); System.out.println("ThreadB get Value "+Tools.t.get()); Thread.sleep(200); } } catch (Exception e) { e.printStackTrace(); } } }
主線程:
public class Run { public static void main(String[] args) { try { ThreadA a=new ThreadA(); ThreadB b=new ThreadB(); a.start(); b.start(); for (int i = 0; i < 100; i++) { Tools.t.set("main "+(i+1)); System.out.println("main get Value "+Tools.t.get()); Thread.sleep(200); } } catch (Exception e) { e.printStackTrace(); } } }
控制臺:
ThreadB get Value ThreadB 1 ThreadA get Value ThreadA 1 main get Value main 1 ThreadA get Value ThreadA 2 main get Value main 2 ThreadB get Value ThreadB 2 main get Value main 3 ThreadA get Value ThreadA 3 ThreadB get Value ThreadB 3 ThreadA get Value ThreadA 4 ThreadB get Value ThreadB 4 main get Value main 4 ThreadB get Value ThreadB 5 ThreadA get Value ThreadA 5 main get Value main 5 main get Value main 6 ThreadB get Value ThreadB 6 ThreadA get Value ThreadA 6
可以發現,ThreadA,ThreadB,和主線程三個在ThreadLocal中存儲的值互不影響,每個線程增加,取值,都是自己的私有的。ThreadLocal中存儲的值具有隔離性。
使用類InheritableThreadLocal類可以讓子線程中取得父線程中的值,並修改。
下面使用ThreadLocal來模擬統計五個線程走完一段代碼消耗的時間的問題。
首先創建一個常用的Profiler類
public class Profiler { //第一次get()方法調用的時候會進行初始化(前提是set方法未調用),每個線程都會調用一次。 private static final ThreadLocal<Long> TIME_THREADLOCAL=new ThreadLocal<Long>(){ protected Long initialValue() { return System.currentTimeMillis(); }; }; public static final void begin(){ TIME_THREADLOCAL.set(System.currentTimeMillis()); } public static final Long end(){ return System.currentTimeMillis()-TIME_THREADLOCAL.get(); } }
主線程中開啟五個線程,並調用begin()和end()方法。(關於未調用set直接調用get返回是null的情況,註釋已經解釋解決辦法。也可以通過繼承ThreadLocal類,然後重寫initialValue()方法改變初始化的值);
public class Run { public static void main(String[] args) { for (int i = 0; i < 5; i++) { final int temp=i; Thread thread=new Thread(new Runnable() { @Override public void run() { try { Profiler.begin(); Thread.sleep(temp*1000); System.out.println("線程"+Thread.currentThread().getName()+"消耗時間 "+Profiler.end()); } catch (Exception e) { e.printStackTrace(); } } }); thread.start(); } } }
控制臺:
線程Thread-0消耗時間 0 線程Thread-1消耗時間 1000 線程Thread-2消耗時間 2000 線程Thread-3消耗時間 3001 線程Thread-4消耗時間 4001
可以發現,五個線程互不影響,各自統計自己的消耗的時間。
每一個優秀的人,都有一段沈默的時光。不抱怨,不訴苦,最後度過那段感動自己的日子。
多線程學習 ThreadLocal的使用。