熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> Java編程 >> Java高級技術 >> 正文

Java線程:深入ThreadLocal

2022-06-13   來源: Java高級技術 

  ThreadLocal與線程成員變量還有區別ThreadLocal該類提供了線程局部變量這個局部變量與一般的成員變量不一樣ThreadLocal的變量在被多個線程使用時候每個線程只能拿到該變量的一個副本這是Java API中的描述通過閱讀API源碼發現並非副本副本什麼概念?克隆品? 或者是別的樣子太模糊

  准確的說應該是ThreadLocal類型的變量內部的注冊表(Map<ThreadT>)發生了變化但ThreadLocal類型的變量本身的確是一個這才是本質!

  下面就做個例子

  一標准例子

  定義了MyThreadLocal類創建它的一個對象tlt分別給四個線程使用結果四個線程tlt變量並沒有出現共用現象二是各用各的這說明四個線程使用的是tlt的副本(克隆品)

  /**

  * 使用了ThreadLocal的類

  *

  * @author leizhimin ::

  */

  public class MyThreadLocal {

  //定義了一個ThreadLocal變量用來保存int或Integer數據

  private ThreadLocal<Integer> tl = new ThreadLocal<Integer>() {

  @Override

  protected Integer initialValue() {

  return ;

  }

  };

  public Integer getNextNum() {

  //將tl的值獲取後加並更新設置t的值

  tlset(tlget() + );

  return tlget();

  }

  }

  /**

  * 測試線程

  *

  * @author leizhimin ::

  */

  public class TestThread extends Thread {

  private MyThreadLocal tlt = new MyThreadLocal();

  public TestThread(MyThreadLocal tlt) {

  thistlt = tlt;

  }

  @Override

  public void run() {

  for (int i = ; i < ; i++) {

  Systemoutprintln(ThreadcurrentThread()getName() + \t + tltgetNextNum());

  }

  }

  }

  /**

  * ThreadLocal測試

  *

  * @author leizhimin ::

  */

  public class Test {

  public static void main(String[] args) {

  MyThreadLocal tlt = new MyThreadLocal();

  Thread t = new TestThread(tlt);

  Thread t = new TestThread(tlt);

  Thread t = new TestThread(tlt);

  Thread t = new TestThread(tlt);

  tstart();

  tstart();

  tstart();

  tstart();

  }

  }

  可以看出三個線程各自獨立編號互不影響

  Thread 

  Thread 

  Thread 

  Thread 

  Thread 

  Thread 

  Thread 

  Thread 

  Thread 

  Thread 

  Thread 

  Thread 

  Process finished with exit code

  tlt對象是一個廢話tl對象也是一個因為組合關系是一對一的但是tl對象內部的Map隨著線程的增多會創建很多Integer對象只是Integer和int已經通用了所以感覺不到Integer的對象屬性

  二不用ThreadLocal

  假如不用ThreadLocal只需要將MyThreadLocal類重新定義為

  /**

  * 使用了ThreadLocal的類

  *

  * @author leizhimin ::

  */

  public class MyThreadLocal {

  private Integer t = ;

  public Integer getNextNum(){

  return t=t+;

  }

  //        //定義了一個ThreadLocal變量用來保存int或Integer數據

  //        private ThreadLocal<Integer> tl = new ThreadLocal<Integer>() {

  //                @Override

  //                protected Integer initialValue() {

  //                        return ;

  //                }

  //        };

  //

  //        public Integer getNextNum() {

  //                //將tl的值獲取後加並更新設置t的值

  //                tlset(tlget() + );

  //                return tlget();

  //        }

  }

  然後運行測試

  Thread 

  Thread 

  Thread 

  Thread 

  Thread 

  Thread 

  Thread 

  Thread 

  Thread 

  Thread 

  Thread 

  Thread 

  Process finished with exit code

  從這裡可以看出四個線程共享了tlt變量結果每個線程都直接修改tlt的屬性

  三自己實現個ThreadLocal

  package comlavasofttest;

  import javautilCollections;

  import javautilHashMap;

  import javautilMap;

  /**

  * 使用了ThreadLocal的類

  *

  * @author leizhimin ::

  */

  public class MyThreadLocal {

  //定義了一個ThreadLocal變量用來保存int或Integer數據

  private comlavasofttestThreadLocal<Integer> tl = new comlavasofttestThreadLocal<Integer>() {

  @Override

  protected Integer initialValue() {

  return ;

  }

  };

  public Integer getNextNum() {

  //將tl的值獲取後加並更新設置t的值

  tlset(tlget() + );

  return tlget();

  }

  }

  class ThreadLocal<T> {

  private Map<Thread T> map = CollectionssynchronizedMap(new HashMap<Thread T>());

  public ThreadLocal() {

  }

  protected T initialValue() {

  return null;

  }

  public T get() {

  Thread t = ThreadcurrentThread();

  T obj = mapget(t);

  if (obj == null && !ntainsKey(t)) {

  obj = initialValue();

  mapput(t obj);

  }

  return obj;

  }

  public void set(T value) {

  mapput(ThreadcurrentThread() value);

  }

  public void remove() {

  mapremove(ThreadcurrentThread());

  }

  }

  運行測試

  Thread 

  Thread 

  Thread 

  Thread 

  Thread 

  Thread 

  Thread 

  Thread 

  Thread 

  Thread 

  Thread 

  Thread 

  Process finished with exit code

  很意外這個山寨版的ThreadLocal也同樣運行很好實現了JavaAPI中ThreadLocal的功能

  四透過現象看本質

  其實從程序角度看tlt變量的確是一個毫無疑問的但是為什麼打印出來的數字就互不影響呢?

  是因為使用了Integer嗎?不是

  原因是protected T initialValue()和get()因為每個線程在調用get()時候發現Map中不存在就創建調用它的時候就創建了一個新變量類型為T每次都新建當然各用個的互不影響了

  為了看清本質將Integer換掉重寫部分類

  package comlavasofttest;

  import javautilCollections;

  import javautilHashMap;

  import javautilMap;

  /**

  * 使用了ThreadLocal的類

  *

  * @author leizhimin ::

  */

  public class MyThreadLocal {

  //定義了一個ThreadLocal變量用來保存int或Integer數據

  //        private ThreadLocal<Bean> tl = new ThreadLocal<Bean>() {

  private comlavasofttestThreadLocal<Bean> tl = new comlavasofttestThreadLocal<Bean>() {

  @Override

  protected Bean initialValue() {

  return new Bean();

  }

  };

  @Override

  public String toString() {

  return MyThreadLocal{ +

  tl= + tl +

  };

  }

  public Bean getBean() {

  return tlget();

  }

  }

  class ThreadLocal<T> {

  private Map<Thread T> map = CollectionssynchronizedMap(new HashMap<Thread T>());

  public ThreadLocal() {

  }

  protected T initialValue() {

  return null;

  }

  public T get() {

  Thread t = ThreadcurrentThread();

  T obj = mapget(t);

  if (obj == null && !ntainsKey(t)) {

  obj = initialValue();

  mapput(t obj);

  }

  return obj;

  }

  public void set(T value) {

  mapput(ThreadcurrentThread() value);

  }

  public void remove() {

  mapremove(ThreadcurrentThread());

  }

  }

  package comlavasofttest;

  /**

  * 測試Bean

  *

  * @author leizhimin ::

  */

  public class Bean {

  private String id = ;

  private String name = none;

  public Bean() {

  }

  public Bean(String id String name) {

  thisid = id;

  thisname = name;

  }

  public String getId() {

  return id;

  }

  public void setId(String id) {

  thisid = id;

  }

  public String getName() {

  return name;

  }

  public void setName(String name) {

  thisname = name;

  }

  public String showinfo() {

  return Bean{ +

  id= + id + \ +

   name= + name + \ +

  };

  }

  }

  package comlavasofttest;

  /**

  * 測試線程

  *

  * @author leizhimin ::

  */

  public class TestThread extends Thread {

  private MyThreadLocal tlt = new MyThreadLocal();

  public TestThread(MyThreadLocal tlt) {

  thistlt = tlt;

  }

  @Override

  public void run() {

  Systemoutprintln(>>>>>: + tlt);

  for (int i = ; i < ; i++) {

  Systemoutprintln(ThreadcurrentThread()getName() + \t +tltgetBean()+\t+tltgetBean()showinfo());

  }

  }

  }

  然後運行測試

  >>>>>:MyThreadLocal{tl=comlavasofttestMyThreadLocal$@defd}

  >>>>>:MyThreadLocal{tl=comlavasofttestMyThreadLocal$@defd}

  >>>>>:MyThreadLocal{tl=comlavasofttestMyThreadLocal$@defd}

  >>>>>:MyThreadLocal{tl=comlavasofttestMyThreadLocal$@defd}

  Thread  comlavasofttestBean@aff  Bean{id= name=none}

  Thread  comlavasofttestBean@feb  Bean{id= name=none}

  Thread  comlavasofttestBean@db  Bean{id= name=none}

  Thread  comlavasofttestBean@feb  Bean{id= name=none}

  Thread  comlavasofttestBean@feb  Bean{id= name=none}

  Thread  comlavasofttestBean@aff  Bean{id= name=none}

  Thread  comlavasofttestBean@db  Bean{id= name=none}

  Thread  comlavasofttestBean@db  Bean{id= name=none}

  Thread  comlavasofttestBean@aff  Bean{id= name=none}

  Thread  comlavasofttestBean@aff  Bean{id= name=none}

  Thread  comlavasofttestBean@aff  Bean{id= name=none}

  Thread  comlavasofttestBean@aff  Bean{id= name=none}

  Process finished with exit code

  從打印結果很清楚的看到MyThreadLocal的tlt對象的確是一個tlt對象裡的ThreadLocal的tl對象也是一個但是將tt給每個線程用的時候線程會重新創建Bean對象加入到ThreadLocal的Map中去使用


From:http://tw.wingwit.com/Article/program/Java/gj/201311/27562.html
    推薦文章
    Copyright © 2005-2022 電腦知識網 Computer Knowledge   All rights reserved.