最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

快速了解Java中ThreadLocal類

 更新時間:2017年11月23日 10:15:56   投稿:mengwei  
這篇文章主要介紹了快速了解Java中ThreadLocal類,介紹了ThreadLocal 是什么,ThreadLocal的作用,ThreadLocal 原理等相關內容,具有一定參考價值,需要的朋友可以了解下。

最近看Android FrameWork層代碼,看到了ThreadLocal這個類,有點兒陌生,就翻了各種相關博客一一拜讀;自己隨后又研究了一遍源碼,發(fā)現(xiàn)自己的理解較之前閱讀的博文有不同之處,所以決定自己寫篇文章說說自己的理解,希望可以起到以下作用:
- 可以疏通研究結果,加深自己的理解;
- 可以起到拋磚引玉的作用,幫助感興趣的同學疏通思路;
- 分享學習經(jīng)歷,同大家一起交流和學習。

一、 ThreadLocal 是什么

ThreadLocal 是Java類庫的基礎類,在包java.lang下面;

官方的解釋是這樣的:
Implements a thread-local storage, that is, a variable for which each thread has its own value. All threads share the same ThreadLocal object, but each sees a different value when accessing it, and changes made by one thread do not affect the other threads. The implementation supports null values.

大致意思是:
可以實現(xiàn)線程的本地存儲機制,ThreadLocal變量是一個不同線程可以擁有不同值的變量。所有的線程可以共享同一個ThreadLocal對象,但是不同線程訪問的時候可以取得不同的值,而且任意一個線程對它的改變不會影響其他線程。類實現(xiàn)是支持null值的(可以在set和get方法傳遞和訪問null值)。

概括來講有三個特性:

- 不同線程訪問時取得不同的值
- 任意線程對它的改變不影響其他線程
- 支持null

下面分別對這些特性進行實例驗證,首先定義一個Test類,在此類中我們鑒證上邊所提到的三個特性。類定義如下:

Test.java

public class Test{
	//定義ThreadLocal 
	private static ThreadLocal name;
	public static void main(String[] args) throws Exception{
		name = new ThreadLocal();
		//Define Thread A
		Thread a = new Thread(){
			public void run(){
				System.out.println("Before invoke set,value is:"+name.get());
				name.set(“Thread A”);
				System.out.println("After invoke set, value is:"+name.get());
			}
		}
		;
		//Define Thread B
		Thread b = new Thread(){
			public void run(){
				System.out.println("Before invoke set,value is :"+name.get());
				name.set(“Thread B”);
				System.out.println("After invoke set,value is :"+name.get());
			}
		}
		;
		// Not invoke set, print the value is null
		System.out.println(name.get());
		// Invoke set to fill a value
		name.set(“Thread Main”);
		// Start thread A
		a.start();
		a.join();
		// Print the value after changed the value by thread A
		System.out.println(name.get());
		// Start thread B
		b.start();
		b.join();
		// Print the value after changed the value by thread B
		System.out.println(name.get())
	}
}

代碼分析:

從定義中我們可以看到只聲明了一個ThreadLocal對象,其他三個線程(主線程、Thread A和Thread B)共享同一個對象;然后,在不同的線程中修改對象的值和在不同的線程中訪問對象的值,并在控制臺輸出查看結果。

看結果:

從控制臺輸出結果可以看到里邊有三個null的輸出,這個是因為在輸出前沒有對對象進行賦值,驗證了支持null的特點;再者,還可以發(fā)現(xiàn)在每個線程我都對對象的值做了修改,但是在其他線程訪問對象時并不是修改后的值,而是訪問線程本地的值;這樣也驗證了其他兩個特點。

二、 ThreadLocal的作用

大家都知道它的使用場景大都是多線程編程,至于具體的作用,這個怎么說那?我覺得這個只能用一個泛的說法來定義,因為一個東西的功能屬性定義了以后會限制大家的思路,就好比說菜刀是用來切菜的,好多人就不會用它切西瓜了。
這里,說下我對它的作用的認識,僅供參考,希望能有所幫助。這樣來描述吧,當一個多線程的程序需要對多數(shù)線程的部分任務(就是run方法里的部分代碼)進行封裝時,在封裝體里就可以用ThreadLocal來包裝與線程相關的成員變量,從而保證線程訪問的獨占性,而且所有線程可以共享一個封裝體對象;可以參考下Android里的Looper。不會用代碼描述問題的程序員不是好程序員;

看代碼:統(tǒng)計線程某段代碼耗時的工具(為說明問題自造)

StatisticCostTime.java

// Class that statistic the cost time
public class StatisticCostTime{
	// record the startTime
	// private ThreadLocal startTime = new ThreadLocal();
	private long startTime;
	// private ThreadLocal costTime = new ThreadLocal();
	private long costTime;
	private StatisticCostTime(){
	}
	//Singleton
	public static final StatisticCostTime shareInstance(){
		return InstanceFactory.instance;
	}
	private static class InstanceFactory{
		private static final StatisticCostTime instance = new StatisticCostTime();
	}
	// start
	public void start(){
		// startTime.set(System. nanoTime ());
		startTime = System.nanoTime();
	}
	// end
	public void end(){
		// costTime.set(System. nanoTime () - startTime.get());
		costTime = System.nanoTime() - startTime;
	}
	public long getStartTime(){
		return startTime;
		// return startTime.get();
	}
	public long getCostTime(){
		// return costTime.get();
		return costTime;
	}

好了,工具設計完工了,現(xiàn)在我們用它來統(tǒng)計一下線程耗時試試唄:

Main.java

public class Main{
	public static void main(String[] args) throws Exception{
		// Define the thread a
		Thread a = new Thread(){
			public void run(){
				try{
					// start record time
					StatisticCostTime.shareInstance().start();
					sleep(200);
					// print the start time of A 
					System.out.println("A-startTime:"+StatisticCostTime.shareInstance().getStartTime());
					// end the record
					StatisticCostTime.shareInstance().end();
					// print the costTime of A   
					System.out.println("A:"+StatisticCostTime.shareInstance().getCostTime());
				}
				catch(Exception e){
				}
			}
		}
		;
		// start a
		a.start();
		// Define thread b
		Thread b = new Thread(){
			public void run(){
				try{
					// record the start time of B1
					StatisticCostTime.shareInstance().start();
					sleep(100);
					// print the start time to console
					System.out.println("B1-startTime:"+StatisticCostTime.shareInstance().getStartTime());
					// end record start time of B1
					StatisticCostTime.shareInstance().end();
					// print the cost time of B1
					System.out.println("B1:"+StatisticCostTime.shareInstance().getCostTime());
					// start record time of B2
					StatisticCostTime.shareInstance().start();
					sleep(100);
					// print start time of B2
					System.out.println("B2-startTime:"+StatisticCostTime.shareInstance().getStartTime());
					// end record time of B2
					StatisticCostTime.shareInstance().end();
					// print cost time of B2
					System.out.println("B2:"+StatisticCostTime.shareInstance().getCostTime());
				}
				catch(Exception e){
				}
			}
		}
		;
		b.start();
	}
}

運行代碼后輸出結果是這樣的
注意:輸出結果精確度為納秒級

看結果是不是和我們預想的不一樣,發(fā)現(xiàn)A的結果應該約等于B1+B2才對呀,怎么變成和B2一樣了那?答案就是我們在定義startTime和costTime變量時,本意是不應共享的,應是線程獨占的才對。而這里變量隨單例共享了,所以當計算A的值時,其實startTime已經(jīng)被B2修改了,所以就輸出了和B2一樣的結果。

現(xiàn)在我們把StatisticCostTime中注釋掉的部分打開,換成ThreadLocal的聲明方式試下。
看結果:

呀!這下達到預期效果了,這時候有同學會說這不是可以線程并發(fā)訪問了嗎,是不是只要我用了ThreadLocal就可以保證線程安全了?答案是no!首先先弄明白為什么會有線程安全問題,無非兩種情況:
1、不該共享的資源,你在線程間共享了;
2、線程間共享的資源,你沒有保證有序訪問;
前者可以用“空間換時間”的方式解決,用ThreadLocal(也可以直接聲明線程局部變量),后者用“時間換空間”的方式解決,顯然這個就不是ThreadLocal力所能及的了。

三、 ThreadLocal 原理

實現(xiàn)原理其實很簡單,每次對ThreadLocal 對象的讀寫操作其實是對線程的Values對象的讀寫操作;這里澄清一下,沒有什么變量副本的創(chuàng)建,因為就沒有用變量分配的內存空間來存T對象的,而是用它所在線程的Values來存T對象的;我們在線程中每次調用ThreadLocal的set方法時,實際上是將object寫入線程對應Values對象的過程;調用ThreadLocal的get方法時,實際上是從線程對應Values對象取object的過程。

看源碼:

ThreadLocal 的成員變量set

/**
 * Sets the value of this variable for the current thread. If set to
 * {@code null}, the value will be set to null and the underlying entry will
 * still be present.
 *
 * @param value the new value of the variable for the caller thread.
 */
public void set(T value) {
  Thread currentThread = Thread.currentThread();
  Values values = values(currentThread);
  if (values == null) {
    values = initializeValues(currentThread);
  }
  values.put(this, value);
}

TreadLocal 的成員方法get

/**
 * Returns the value of this variable for the current thread. If an entry
 * doesn't yet exist for this variable on this thread, this method will
 * create an entry, populating the value with the result of
 * {@link #initialValue()}.
 *
 * @return the current value of the variable for the calling thread.
 */
@SuppressWarnings("unchecked")
public T get() {
  // Optimized for the fast path.
  Thread currentThread = Thread.currentThread();
  Values values = values(currentThread);
  if (values != null) {
    Object[] table = values.table;
    int index = hash & values.mask;
    if (this.reference == table[index]) {
      return (T) table[index + 1];
    }
  } else {
    values = initializeValues(currentThread);
  }
 
  return (T) values.getAfterMiss(this);
}

ThreadLocal的成員方法initializeValues

/**
 * Creates Values instance for this thread and variable type.
 */
Values initializeValues(Thread current) {
  return current.localValues = new Values();
}

ThreadLocal 的成員方法values

/**
 * Gets Values instance for this thread and variable type.
 */
Values values(Thread current) {
  return current.localValues;
}

那這個Values又是怎樣讀寫Object那?

Values是作為ThreadLocal的內部類存在的;這個Values里包括了一個重要數(shù)組Object[],這個數(shù)據(jù)就是解答問題的關鍵部分,它是用來存儲線程本地各種類型TreadLocal變量用的;那么問題來了,具體取某個類型的變量時是怎么保證不取到其他類型的值那?按一般的做法會用一個Map根據(jù)key-value映射一下的;對的,思路就是這個思路,但是這里并沒有用Map來實現(xiàn),是用一個Object[]實現(xiàn)的Map機制;但是,若要用Map理解的話,也是不可以的,因為機制是相同的;key其實上對應ThreadLocal的弱引用,value就對應我們傳進去的Object。

解釋下是怎么用Object[]實現(xiàn)Map機制的(參考圖1);它是用數(shù)組下標的奇偶來區(qū)分key和value的,就是下表是偶數(shù)的位置存儲key,奇數(shù)存儲value,就是這樣搞得;感興趣的同學如果想知道算法實現(xiàn)的話,可以深入研究一下,這里我不在詳述了。

結合前面第一個實例分析下存儲情況:

當程序執(zhí)行時存在A,B和main三個線程,分別在線程中調用name.set()時同時針對三個線程實例在堆區(qū)分配了三塊相同的內存空間來存儲Values對象,以name引用作為key,具體的object作為值存進三個不同的Object[](參看下圖):

四、 總結

ThreadLocal 不能完全解決多線程編程時的并發(fā)問題,這種問題還要根據(jù)不同的情況選擇不同的解決方案,“空間換時間”還是“時間換空間”。

ThreadLocal最大的作用就是把線程共享變量轉換成線程本地變量,實現(xiàn)線程之間的隔離。

以上就是本文關于快速了解Java中ThreadLocal的全部內容,希望對大家有所幫助。如有不足之處,歡迎留言指出。感謝朋友們對本站的支持。

相關文章

  • IntelliJ IDEA中折疊所有Java代碼,再也不怕大段的代碼了

    IntelliJ IDEA中折疊所有Java代碼,再也不怕大段的代碼了

    今天小編就為大家分享一篇關于IntelliJ IDEA中折疊所有Java代碼,再也不怕大段的代碼了,小編覺得內容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2018-10-10
  • 深入了解Java?Synchronized鎖升級過程

    深入了解Java?Synchronized鎖升級過程

    java中的鎖是針對對象而言的,它鎖住的是一個對象,并且具有可重入的性質,下面這篇文章主要給大家介紹了關于Java?Synchronized鎖升級過程的相關資料,需要的朋友可以參考下
    2022-03-03
  • SpringBoot3整合 Elasticsearch 8.x 使用Repository構建增刪改查示例應用

    SpringBoot3整合 Elasticsearch 8.x 使用Repository構

    我們構建了一個完整的 Spring Boot 3 和 Elasticsearch 8.x 的增刪改查示例應用,使用 Spring Data Elasticsearch Repository,我們能夠快速實現(xiàn)對 Elasticsearch 的基本 CRUD 操作,簡化了開發(fā)流程,希望這個示例能夠幫助你理解如何在項目中有效使用 Elasticsearch!
    2024-11-11
  • Java中的volatile關鍵字原理深入解析

    Java中的volatile關鍵字原理深入解析

    這篇文章主要介紹了Java中的volatile關鍵字原理深入解析,volatile是Java 編程語言允許線程訪問共享變量,為了確保共享變量能被準確和一致地更新,線程應該確保通過排他鎖單獨獲得這個變量,需要的朋友可以參考下
    2023-12-12
  • 基于spring boot 命令行啟動的一些坑

    基于spring boot 命令行啟動的一些坑

    這篇文章主要介紹了spring boot 命令行啟動的一些坑,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • JAVA實現(xiàn)經(jīng)典掃雷游戲的示例代碼

    JAVA實現(xiàn)經(jīng)典掃雷游戲的示例代碼

    windows自帶的游戲《掃雷》是陪伴了無數(shù)人的經(jīng)典游戲,本程序參考《掃雷》的規(guī)則進行了簡化,用java語言實現(xiàn),采用了swing技術進行了界面化處理。感興趣的可以學習一下
    2022-01-01
  • Java8中新特性Optional、接口中默認方法和靜態(tài)方法詳解

    Java8中新特性Optional、接口中默認方法和靜態(tài)方法詳解

    Java 8 已經(jīng)發(fā)布很久了,很多報道表明Java 8 是一次重大的版本升級。下面這篇文章主要給大家介紹了關于Java8中新特性Optional、接口中默認方法和靜態(tài)方法的相關資料,文中通過示例代碼介紹的非常詳細,需要的朋友可以參考下。
    2017-12-12
  • Flink DataStream基礎框架源碼分析

    Flink DataStream基礎框架源碼分析

    這篇文章主要為大家介紹了Flink DataStream基礎框架源碼分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-12-12
  • Java編程用棧來求解漢諾塔問題的代碼實例(非遞歸)

    Java編程用棧來求解漢諾塔問題的代碼實例(非遞歸)

    這篇文章主要介紹了Java編程用棧來求解漢諾塔問題的代碼實例(非遞歸),具有一定參考價值,這里給大家分享下,供朋友們參考。
    2017-10-10
  • Java Swing組件實現(xiàn)進度監(jiān)視功能示例

    Java Swing組件實現(xiàn)進度監(jiān)視功能示例

    這篇文章主要介紹了Java Swing組件實現(xiàn)進度監(jiān)視功能,結合完整實例形式詳細分析了Java基于Swing組件實現(xiàn)進度條顯示功能的具體操作技巧與相關注意事項,需要的朋友可以參考下
    2018-02-02

最新評論

吴桥县| 叶城县| 安新县| 马尔康县| 白城市| 县级市| 塔河县| 同仁县| 理塘县| 岳阳县| 仙居县| 焦作市| 苗栗县| 张掖市| 延寿县| 鄂州市| 轮台县| 靖州| 崇州市| 修水县| 剑川县| 翼城县| 中山市| 南澳县| 黎城县| 巢湖市| 从江县| 东海县| 定陶县| 平遥县| 武汉市| 贡觉县| 金华市| 儋州市| 乐昌市| 吉安县| 滕州市| 鞍山市| 政和县| 双牌县| 长武县|