中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

使用ThreadLocal的作用有哪些

發布時間:2020-11-30 17:33:58 來源:億速云 閱讀:357 作者:Leah 欄目:編程語言

本篇文章為大家展示了使用ThreadLocal的作用有哪些,內容簡明扼要并且容易理解,絕對能使你眼前一亮,通過這篇文章的詳細介紹希望你能有所收獲。

ThreadLocal,直譯為“線程本地”或“本地線程”,如果你真的這么認為,那就錯了!其實,它就是一個容器,用于存放線程的局部變量,我認為應該叫做 ThreadLocalVariable(線程局部變量)才對,真不理解為什么當初 Sun 公司的工程師這樣命名。
早在 JDK 1.2 的時代,java.lang.ThreadLocal 就誕生了,它是為了解決多線程并發問題而設計的,只不過設計得有些難用,所以至

今沒有得到廣泛使用。其實它還是挺有用的,不相信的話,我們一起來看看這個例子吧。

一個序列號生成器的程序,可能同時會有多個線程并發訪問它,要保證每個線程得到的序列號都是自增的,而不能相互干擾。

先定義一個接口:

public interface Sequence {

 int getNumber();
}

每次調用 getNumber() 方法可獲取一個序列號,下次再調用時,序列號會自增。
再做一個線程類:

public class ClientThread extends Thread {

 private Sequence sequence;

 public ClientThread(Sequence sequence) {
  this.sequence = sequence;
 }

 @Override
 public void run() {
  for (int i = 0; i < 3; i++) {
   System.out.println(Thread.currentThread().getName() + " => " + sequence.getNumber());
  }
 }
}

在線程中連續輸出三次線程名與其對應的序列號。
我們先不用 ThreadLocal,來做一個實現類吧。

public class SequenceA implements Sequence {

 private static int number = 0;

 public int getNumber() {
  number = number + 1;
  return number;
 }

 public static void main(String[] args) {
  Sequence sequence = new SequenceA();

  ClientThread thread1 = new ClientThread(sequence);
  ClientThread thread2 = new ClientThread(sequence);
  ClientThread thread3 = new ClientThread(sequence);

  thread1.start();
  thread2.start();
  thread3.start();
 }
}

序列號初始值是0,在 main() 方法中模擬了三個線程,運行后結果如下:
Thread-0 => 1
Thread-0 => 2
Thread-0 => 3
Thread-2 => 4
Thread-2 => 5
Thread-2 => 6
Thread-1 => 7
Thread-1 => 8
Thread-1 => 9

由于線程啟動順序是隨機的,所以并不是0、1、2這樣的順序,這個好理解。為什么當 Thread-0 輸出了1、2、3之后,而 Thread-2 卻輸出了4、5、6呢?線程之間竟然共享了 static 變量!這就是所謂的“非線程安全”問題了。

那么如何來保證“線程安全”呢?對應于這個案例,就是說不同的線程可擁有自己的 static 變量,如何實現呢?下面看看另外一個實現吧。

public class SequenceB implements Sequence {

 private static ThreadLocal<Integer> numberContainer = new ThreadLocal<Integer>() {
  @Override
  protected Integer initialValue() {
   return 0;
  }
 };

 public int getNumber() {
  numberContainer.set(numberContainer.get() + 1);
  return numberContainer.get();
 }

 public static void main(String[] args) {
  Sequence sequence = new SequenceB();

  ClientThread thread1 = new ClientThread(sequence);
  ClientThread thread2 = new ClientThread(sequence);
  ClientThread thread3 = new ClientThread(sequence);

  thread1.start();
  thread2.start();
  thread3.start();
 }
}

通過 ThreadLocal 封裝了一個 Integer 類型的 numberContainer 靜態成員變量,并且初始值是0。再看 getNumber() 方法,首先從 numberContainer 中 get 出當前的值,加1,隨后 set 到 numberContainer 中,最后將 numberContainer 中 get 出當前的值并返回。

是不是很惡心?但是很強大!確實稍微饒了一下,我們不妨把 ThreadLocal 看成是一個容器,這樣理解就簡單了。所以,這里故意用 Container 這個單詞作為后綴來命名 ThreadLocal 變量。

運行結果如何呢?看看吧。

Thread-0 => 1
Thread-0 => 2
Thread-0 => 3
Thread-2 => 1
Thread-2 => 2
Thread-2 => 3
Thread-1 => 1
Thread-1 => 2
Thread-1 => 3

每個線程相互獨立了,同樣是 static 變量,對于不同的線程而言,它沒有被共享,而是每個線程各一份,這樣也就保證了線程安全。 也就是說,TheadLocal 為每一個線程提供了一個獨立的副本!

搞清楚 ThreadLocal 的原理之后,有必要總結一下 ThreadLocal 的 API,其實很簡單。
1.public void set(T value):將值放入線程局部變量中
2.public T get():從線程局部變量中獲取值
3.public void remove():從線程局部變量中移除值(有助于 JVM 垃圾回收)
4.protected T initialValue():返回線程局部變量中的初始值(默認為 null) 

為什么 initialValue() 方法是 protected 的呢?就是為了提醒程序員們,這個方法是要你們來實現的,請給這個線程局部變量一個初始值吧。

了解了原理與這些 API,其實想想 ThreadLocal 里面不就是封裝了一個 Map 嗎?自己都可以寫一個 ThreadLocal 了,嘗試一下吧。

public class MyThreadLocal<T> {

 private Map<Thread, T> container = Collections.synchronizedMap(new HashMap<Thread, T>());

 public void set(T value) {
  container.put(Thread.currentThread(), value);
 }

 public T get() {
  Thread thread = Thread.currentThread();
  T value = container.get(thread);
  if (value == null && !container.containsKey(thread)) {
   value = initialValue();
   container.put(thread, value);
  }
  return value;
 }

 public void remove() {
  container.remove(Thread.currentThread());
 }

 protected T initialValue() {
  return null;
 }
}

以上完全山寨了一個 ThreadLocal,其中中定義了一個同步 Map(為什么要這樣?請讀者自行思考),代碼應該非常容易讀懂。
下面用這 MyThreadLocal 再來實現一把看看。

public class SequenceC implements Sequence {

 private static MyThreadLocal<Integer> numberContainer = new MyThreadLocal<Integer>() {
  @Override
  protected Integer initialValue() {
   return 0;
  }
 };

 public int getNumber() {
  numberContainer.set(numberContainer.get() + 1);
  return numberContainer.get();
 }

 public static void main(String[] args) {
  Sequence sequence = new SequenceC();

  ClientThread thread1 = new ClientThread(sequence);
  ClientThread thread2 = new ClientThread(sequence);
  ClientThread thread3 = new ClientThread(sequence);

  thread1.start();
  thread2.start();
  thread3.start();
 }
}

以上代碼其實就是將 ThreadLocal 替換成了 MyThreadLocal,僅此而已,運行效果和之前的一樣,也是正確的。
其實 ThreadLocal 可以單獨成為一種設計模式,就看你怎么看了。 

ThreadLocal 具體有哪些使用案例呢?通過 ThreadLocal 存放 JDBC Connection,以達到事務控制的能力。

上述內容就是使用ThreadLocal的作用有哪些,你們學到知識或技能了嗎?如果還想學到更多技能或者豐富自己的知識儲備,歡迎關注億速云行業資訊頻道。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

阿克苏市| 大港区| 南漳县| 河津市| 子洲县| 商城县| 巴楚县| 义马市| 靖远县| 双流县| 平罗县| 新巴尔虎左旗| 安塞县| 德安县| 香格里拉县| 南澳县| 蒙山县| 横山县| 红河县| 江达县| 垫江县| 原阳县| 鸡西市| 杨浦区| 抚顺县| 绿春县| 安溪县| 哈尔滨市| 双辽市| 大同市| 措勤县| 东海县| 鄯善县| 政和县| 特克斯县| 定结县| 塔河县| 米脂县| 搜索| 新巴尔虎左旗| 原平市|