精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

聊聊圖解多線程

網絡 通信技術
進程的本質是一個正在執行的程序,程序運行時系統會創建一個進程,并且「給每個進程分配獨立的內存地址空間,用來保證每個進程地址不會相互干擾」。

 [[360353]]

進程與線程

「進程」

進程的本質是一個正在執行的程序,程序運行時系統會創建一個進程,并且「給每個進程分配獨立的內存地址空間,用來保證每個進程地址不會相互干擾」。

同時,在 CPU 對進程做時間片的切換時,保證進程切換過程中仍然要從進程切換之前運行的位置處開始執行。所以進程通常還會包括程序計數器、堆棧指針。

相對好理解點的案例:電腦上開啟QQ就是開啟一個進程、打開IDEA就是開啟一個進程、打開瀏覽器就是開啟一個進程.....

當我們的電腦開啟你太多的運用(QQ,微信,瀏覽器、PDF、word、IDEA等)后,電腦就很容易出現卡頓,甚至死機,這里最主要原因就是CPU一直不停地切換導致的。

下圖是單核CPU情況下,多進程之間的切換:

有了進程以后,可以讓操作系統從宏觀層面實現多應用并發。

而并發的實現是通過 CPU 時間片不端切換執行的,對于單核 CPU來說,在任意一個時刻只會有一個進程在被CPU 調度。

線程的生命周期

既然是生命周期,那么就很有可能會有階段性的或者狀態的,比如人的一生一樣:

精子和卵子結合---> 嬰兒---> 小孩--> 成年--> 中年--> 老年-->去世

線程狀態

關于線程的生命周期網上有不一樣的答案,有說五種也有說六種。

Java中線程確實有6種,這是有理有據的,可以看看java.lang.Thread類中有個這么一個枚舉。

  1. public enum State { 
  2.         NEW, 
  3.         RUNNABLE, 
  4.         BLOCKED,  
  5.         WAITING,  
  6.         TIMED_WAITING,  
  7.         TERMINATED; 

這就是Java線程對應的狀態,組合起來就是Java中一個線程的生命周期。下面是這個枚舉的注釋:

每種狀態簡單說明:

  • NEW(初始):線程被創建后尚未啟動。
  • RUNNABLE(運行):包括了操作系統線程狀態中的Running和Ready,也就是處于此狀態的線程可能正在運行,也可能正在等待系統資源,如等待CPU為它分配時間片。
  • BLOCKED(阻塞):線程阻塞于鎖。
  • WAITING(等待):線程需要等待其他線程做出一些特定動作(通知或中斷)。
  • TIME_WAITING(超時等待):該狀態不同于WAITING,它可以在指定的時間內自行返回。
  • TERMINATED(終止):該線程已經執行完畢。

線程生命周期

借用網上的這張圖,這張圖描述的很清楚了,這里就不在啰嗦。

何為線程安全?

我們經常會聽說某個類是線程安全,某個類不是線程安全的。那么究竟什么叫做線程安全呢?

我們引用《Java Concurrency in Practice》里面的定義:

在不使用額外同步的情況下,多個線程訪問一個對象時,不論線程之間如何交替執行或者在調用方進行任何其它的協調操作,調用這個對象的行為都能得到正確的結果,那么這個對象是線程安全的。

也可以這么理解:

多個線程訪問同一個對象時,如果不用考慮這些線程在運行時環境下的調度和交替執行,也不需要進行額外的同步,或者在調用方進行任何其他操作,調用這個對象的行為都可以獲得正確的結果,那么這個對象就是線程安全的。或者說:一個類或者程序所提供的接口對于線程來說是原子操作或者多個線程之間的切換不會導致該接口的執行結果存在二義性,也就是說我們不用考慮同步的問題。

可以簡單的理解為:“你隨便怎么調用,出了問題算我輸”。

這個定義對于類來說是十分嚴格的,即使是Java API中標為線程安全的類也很難滿足這個要求。

比如Vector是標記為線程安全的,但實際上并不能滿足這個條件,舉個例子:

  1. public class Vector<E>  extends AbstractList<E>  implements List<E>, RandomAccess, Cloneable, java.io.Serializable
  2.     public synchronized E get(int index) { 
  3.         if (index >= elementCount) 
  4.             throw new ArrayIndexOutOfBoundsException(index); 
  5.         return elementData(index); 
  6.     } 
  7.     public synchronized void removeElementAt(int index) { 
  8.         modCount++; 
  9.         if (index >= elementCount) { 
  10.             throw new ArrayIndexOutOfBoundsException(index + " >= " + 
  11.                                                      elementCount); 
  12.         } 
  13.         else if (index < 0) { 
  14.             throw new ArrayIndexOutOfBoundsException(index); 
  15.         } 
  16.         int j = elementCount - index - 1; 
  17.         if (j > 0) { 
  18.             System.arraycopy(elementData, index + 1, elementData, index, j); 
  19.         } 
  20.         elementCount--; 
  21.         elementData[elementCount] = null; /* to let gc do its work */ 
  22.     } 
  23.     //....基本上所有方法都是synchronized修飾的 
  24. }    

來看下面一個案例:

判斷Vector中第0個元素是不是空字符,如果是空字符就將其刪除。

  1. package com.java.tian.blog.utils; 
  2.  
  3. import java.util.Vector; 
  4.  
  5. public class SynchronizedDemo{ 
  6.     static Vector<String> vct = new Vector<String>(); 
  7.     public  void remove() { 
  8.         if("".equals(vct.get(0))) { 
  9.             vct.remove(0); 
  10.         } 
  11.     } 
  12.  
  13.     public static void main(String[] args) { 
  14.         vct.add(""); 
  15.         SynchronizedDemo synchronizedDemo = new SynchronizedDemo(); 
  16.         new Thread(new Runnable() { 
  17.             @Override 
  18.             public void run() { 
  19.                 synchronizedDemo.remove(); 
  20.             } 
  21.         },"線程1").start(); 
  22.         new Thread(new Runnable() { 
  23.             @Override 
  24.             public void run() { 
  25.                 synchronizedDemo.remove(); 
  26.             } 
  27.         },"線程2").start(); 
  28.  
  29.     } 

上面的邏輯看起來沒有問題,實際上是有可能導致錯誤的:假設第0個元素是空字符,判斷的時候得到的結果是true。

兩個線程同時執行上面的remove方法,(「極端的情況」)都「可能」get到的是"",然后都去刪除第0個元素,這個元素有可能已經被其它線程刪除了,因此Vector不是絕對線程安全的。(上面這個案例只是做演示而已,在你的業務代碼里面這么寫的話,線程安全真的就不能靠Vector來保證了)。

通常情況下我們說的線程安全都是相對線程安全,相對線程安全只要求調用單個方法的時候不需要同步就可以得到正確的結果,但數多個方法組合調用的時候也是有可能導致多線程問題的。

如果想讓上面的操作執行正確,我們需要在調用Vector方法的時候添加額外的同步操作:

  1. package com.java.tian.blog.utils; 
  2.  
  3. import java.util.Vector; 
  4.  
  5. public class SynchronizedDemo { 
  6.     static Vector<String> vct = new Vector<String>(); 
  7.  
  8.     public void remove() { 
  9.         synchronized (vct) { 
  10.         //synchronized (SynchronizedDemo.class) { 
  11.             if ("".equals(vct.get(0))) { 
  12.                 vct.remove(0); 
  13.             } 
  14.         } 
  15.     } 
  16.  
  17.     public static void main(String[] args) { 
  18.         vct.add(""); 
  19.         SynchronizedDemo synchronizedDemo = new SynchronizedDemo(); 
  20.         new Thread(new Runnable() { 
  21.             @Override 
  22.             public void run() { 
  23.                 synchronizedDemo.remove(); 
  24.             } 
  25.         }, "線程1").start(); 
  26.         new Thread(new Runnable() { 
  27.             @Override 
  28.             public void run() { 
  29.                 synchronizedDemo.remove(); 
  30.             } 
  31.         }, "線程2").start(); 
  32.     } 

根據Vector的源代碼可知:Vector的每個方法都使用了synchronized關鍵字修飾,因此鎖對象就是這個對象本身。在上面的代碼中我們嘗試獲取的也是vct對象的鎖,可以和vct對象的其它方法互斥,因此這樣做可以保證得到正確的結果。

如果Vector內部使用的是其它鎖同步的,并封裝了鎖對象,那么我們無論如何都無法正確執行這個“先判斷后修改”的操作。

假設被封裝的對象鎖為obj,get()和remove()方法對應的鎖都是obj,而整個操作過程獲取的是vct的鎖,一個線程調用get()方法成功后就釋放了obj的鎖,這時這個線程只持有vct的鎖,而其它線程可以獲得obj的鎖并搶先一步刪除了第0個元素。

Java為開發者提供了很多強大的工具類,這些工具類里面有的是線程安全的,有的不是線程安全的。在這里我們列舉幾個面試常考的:

線程安全的類:Vector、Hashtable、StringBuffer

非線程安全的類:ArrayList、HashMap、StringBuilder

有人可能會反問:為什么Java不把所有的類都設計成線程安全的呢?這樣對于我們開發者來說豈不是更爽嗎?我們就不用考慮什么線程安全問題了。

事情都是具有兩面性的,獲得線程安全但是性能會有所下降,畢竟鎖的開銷是擺在那里的。線程不安全但是性能會有所提升,具體場景還得看業務更偏向于哪一個。

一個問題引發的思考:

  1. public class SynchronizedDemo { 
  2.  
  3.     static int count
  4.  
  5.     public void incre() { 
  6.         try { 
  7.             //每個線程都睡一會,模仿業務代碼 
  8.             Thread.sleep(100 ); 
  9.         } catch (InterruptedException e) { 
  10.             e.printStackTrace(); 
  11.         } 
  12.         count++; 
  13.     } 
  14.  
  15.     public static void main(String[] args) { 
  16.         SynchronizedDemo synchronizedDemo = new SynchronizedDemo(); 
  17.  
  18.         for (int i = 0; i < 1000; i++) { 
  19.             new Thread(new Runnable() { 
  20.                 @Override 
  21.                 public void run() { 
  22.                     synchronizedDemo.incre(); 
  23.                 } 
  24.             }).start(); 
  25.         } 
  26.         try { 
  27.             //讓主線程等待所有線程執行完畢 
  28.             Thread.sleep(2000L); 
  29.         } catch (InterruptedException e) { 
  30.             e.printStackTrace(); 
  31.         } 
  32.         System.out.println(count); 
  33.     } 

上面這段代碼輸出的結果是不確定的,結果是小于等于1000。

1000線程都去對count進行++操作。

對象內存布局

對象在內存中的存儲可以分為 3 塊區域,分別是對象頭、實例數據和對齊填充。

其中,對象頭包括兩部分內容,一部分是對象本身的運行時數據,像 GC 分代年齡、哈希碼、鎖狀態標識等等,官方稱之為“Mark Word”,如果忽略壓縮指針的影響,這部分數據在 32 位和 64 位的虛擬機中分別占 32 位和 64 位。

但是對象需要存儲的運行時數據很多,32 位或者 64 位都不一定能存的下,考慮到虛擬機的空間效率,這個 Mark Word 被設計成一個非固定的數據結構,它會根據對象的狀態復用自己的存儲空間,對象處于不同狀態的時候,對應的 bit 表示的含義可能會不一樣,見下圖,以 32 位 Hot Spot 虛擬機為例:

從上圖中我們可以看出,如果對象處于未鎖定狀態(無鎖態),那么 Mark Word 的 25 位用于存儲對象的哈希碼,4 位用于存儲對象分代年齡,1 位固定為 0,兩位用于存儲鎖標志位。

這個圖對于理解后面提到的輕量級鎖、偏向鎖是非常重要的,當然我們現在可以先著重考慮對象處于重量級鎖狀態下的情況,也就是鎖標志位為 10。同時我們看到,無鎖態和偏向鎖狀態下,2 位鎖標志位都是“01”,留有 1 位表示是否可偏向,我們姑且叫它“偏向位”。

「注」:對象頭的另一部分則是類型指針,虛擬機可以通過這個指針來確認該對象是哪個類的實例。但是我們要注意,并不是所有的虛擬機都必須以這種方式來確定對象的元數據信息。對象的訪問定位一般有句柄和直接指針兩種,如果使用句柄的話,那么對象的元數據信息可以直接包含在句柄中(當然也包括對象實例數據的地址信息),也就沒必要將這些元數據和實例數據存儲在一起了。至于實例數據和對齊填充,這里暫不做討論。

前面我們提到了,Java 中的每個對象都與一個 monitor 相關聯,當鎖標志位為 10 時,除了 2bit 的標志位,指向的就是 monitor 對象的地址(還是以 32 位虛擬機為例)。這里我們可以翻閱一下 OpenJDK 的源碼,如果我們需要下載openJDK的源碼:

找到。這里先看一下markOpp.hpp文件。該文件的相對路徑為:

  1. openjdk\hotspot\src\share\vm\oops 

下圖是文件中的注釋部分:

我們可以看到,其中描述了 32 位和 64 位下 Mark World 的存儲狀態。也可以看到64位下,前25位是沒有使用的。

我們也可以看到 markOop.hpp 中定義的鎖狀態枚舉,對應我們前面提到的無鎖、偏向鎖、輕量級鎖、重量級鎖(膨脹鎖)、GC 標記等:

  1. enum { locked_value             = 0,//00 輕量級鎖 
  2.        unlocked_value           = 1,//01 無鎖 
  3.        monitor_value            = 2,//10 重量級鎖 
  4.        marked_value             = 3,//11 GC標記 
  5.        biased_lock_pattern      = 5 //101 偏向鎖,1位偏向標記和2位狀態標記(01) 
  6. }; 

從注釋中,我們也可以看到對其的簡要描述,后面會我們詳細解釋:

這里我們的重心還是是重量級鎖,所以我們看看源碼中 monitor 對象是如何定義的,對應的頭文件是 objectMonitor.hpp,文件路徑為:

  1. openjdk\hotspot\src\share\vm\runtime 

我們來簡單看一下這個 objectMonitor.hpp 的定義:

  1. // initialize the monitor, exception the semaphore, all other fields 
  2. // are simple integers or pointers 
  3. ObjectMonitor() { 
  4.   _header       = NULL
  5.   _count        = 0; 
  6.   _waiters      = 0,//等待線程數 
  7.   _recursions   = 0;//重入次數 
  8.   _object       = NULL
  9.   _owner        = NULL;//持有鎖的線程(邏輯上,實際上除了THREAD,還可能是Lock Record) 
  10.   _WaitSet      = NULL;//線程wait之后會進入該列表 
  11.   _WaitSetLock  = 0 ; 
  12.   _Responsible  = NULL ; 
  13.   _succ         = NULL ; 
  14.   _cxq          = NULL ;//等待獲取鎖的線程列表,和_EntryList配合使用 
  15.   FreeNext      = NULL ; 
  16.   _EntryList    = NULL ;//等待獲取鎖的線程列表,和_cxq配合使用 
  17.   _SpinFreq     = 0 ; 
  18.   _SpinClock    = 0 ; 
  19.   OwnerIsThread = 0 ;//當前持有者是否為THREAD類型,如果是輕量級鎖膨脹而來,還沒有enter的話, 
  20.                      //_owner存儲的可能會是Lock Record 
  21.   _previous_owner_tid = 0; 

簡單的說,當多個線程競爭訪問同一段同步代碼塊時,如果線程獲取到了 monitor,那么就會把 _owner 設置成當前線程,如果是重入的話,_recursions 會加 1,如果獲取 monitor 失敗,則會進入 _cxq隊列。

鎖被釋放時,_cxq中的線程會被移動到 _EntryList中,并且喚醒_EntryList 隊首線程。當然,選取喚醒線程有幾個不同的策略(Knob_QMode),還是后面結合源碼解析。

「注」:_cxq和 _EntryList本質上是ObjectWaiter 類型,它本質上其實是一個雙向鏈表 (具有前后指針),只是在使用的時候不一定要當做雙向鏈表使用,比如 _cxq 是當做單向鏈表使用的,_EntryList是當做雙向鏈表使用的。

什么場景會導致線程的上下文切換?

導致線程上下文切換的有兩種類型:

自發性上下文切換是指線程由 Java 程序調用導致切出,在多線程編程中,執行調用上圖中的方法或關鍵字,常常就會引發自發性上下文切換。

非自發性上下文切換指線程由于調度器的原因被迫切出。常見的有:線程被分配的時間片用完,虛擬機垃圾回收導致或者執行優先級的問題導致。

waity /notify

注意兩個隊列:

等待隊列:notifyAll/notify喚醒的就是等待隊列中的線程;

同步線程:就是競爭鎖的所有線程,等待隊列中的線程被喚醒后進入同步隊列。

sleep與wait的區別

sleep

  • 讓當前線程休眠指定時間。
  • 休眠時間的準確性依賴于系統時鐘和CPU調度機制。
  • 不釋放已獲取的鎖資源,如果sleep方法在同步上下文中調用,那么其他線程是無法進入到當前同步塊或者同步方法中的。
  • 可通過調用interrupt()方法來喚醒休眠線程。
  • sleep是Thread里的方法

wait

  • 讓當前線程進入等待狀態,當別的其他線程調用notify()或者notifyAll()方法時,當前線程進入就緒狀態
  • wait方法必須在同步上下文中調用,例如:同步方法塊或者同步方法中,這也就意味著如果你想要調用wait方法,前提是必須獲取對象上的鎖資源
  • 當wait方法調用時,當前線程將會釋放已獲取的對象鎖資源,并進入等待隊列,其他線程就可以嘗試獲取對象上的鎖資源。
  • wait是Object中的方法

樂觀鎖、悲觀鎖、可重入鎖.....

作為一個Java開發多年的人來說,肯定多多少少熟悉一些鎖,或者聽過一些鎖。今天就來做一個鎖相關總結。

悲觀鎖和樂觀鎖

悲觀鎖

顧名思義,他就是很悲觀,把事情都想的最壞,是指該鎖只能被一個線程鎖持有,如果A線程獲取到鎖了,這時候線程B想獲取鎖只能排隊等待線程A釋放。

在數據庫中這樣操作:

  1. select user_name,user_pwd from t_user for update

樂觀鎖

顧名思義,樂觀,人樂觀就是什么是都想得開,船到橋頭自然直。樂觀鎖就是我都覺得他們都沒有拿到鎖,只有我拿到鎖了,最后再去問問這個鎖真的是我獲取的嗎?是就把事情給干了。

典型的代表:CAS=Compare and Swap 先比較哈,資源是不是我之前看到的那個,是那我就把他換成我的。不是就算了。

在Java中java.util.concurrent.atomic包下面的原子變量就是使用了樂觀鎖的一種實現方式CAS實現。

通常都是 使用version、時間戳等來比較是否已被其他線程修改過。

使用悲觀鎖還是使用樂觀鎖?

在樂觀鎖與悲觀鎖的選擇上面,主要看下兩者的區別以及適用場景就可以了。

「響應效率」

如果需要非常高的響應速度,建議采用樂觀鎖方案,成功就執行,不成功就失敗,不需要等待其他并發去釋放鎖。樂觀鎖并未真正加鎖,效率高。一旦鎖的粒度掌握不好,更新失敗的概率就會比較高,容易發生業務失敗。

「沖突頻率」

如果沖突頻率非常高,建議采用悲觀鎖,保證成功率。沖突頻率大,選擇樂觀鎖會需要多次重試才能成功,代價比較大?!钢卦嚧鷥r」

如果重試代價大,建議采用悲觀鎖。悲觀鎖依賴數據庫鎖,效率低。更新失敗的概率比較低。

樂觀鎖如果有人在你之前更新了,你的更新應當是被拒絕的,可以讓用戶從新操作。悲觀鎖則會等待前一個更新完成。這也是區別。

公平鎖和非公平鎖

公平鎖

顧名思義,是公平的,先來先得,FIFO;必須遵守排隊規則。不能僭越。多個線程按照申請鎖的順序去獲得鎖,線程會直接進入隊列去排隊,永遠都是隊列的第一位才能得到鎖。

在ReentrantLock中默認使用的非公平鎖,但是可以在構建ReentrantLock實例時候指定為公平鎖。

  1. ReentrantLock fairSyncLock = new ReentrantLock(true); 

假設線程 A 已經持有了鎖,這時候線程 B 請求該鎖將會被掛起,當線程 A 釋放鎖后,假如當前有線程 C 也需要獲取該鎖,那么在公平鎖模式下,獲取鎖和釋放鎖的步驟為:

  1. 線程A獲取鎖--->線程A釋放鎖
  2. 線程B獲取鎖--->線程B釋放鎖;
  3. 線程C獲取鎖--->線程釋放鎖;

優點」

所有的線程都能得到資源,不會餓死在隊列中。

「缺點」

吞吐量會下降很多,隊列里面除了第一個線程,其他的線程都會阻塞,CPU喚醒阻塞線程的開銷會很大。

非公平鎖

顧名思義,老子才不管你們誰先排隊的,也就是平時大家在生活中很討厭的。生活中排隊的很多,上車排隊、坐電梯排隊、超市結賬付款排隊等等。但是不是每個人都會遵守規則站著排隊,這就對站著排隊的人來說就不公平了。等搶不到后再去乖乖排隊。

多個線程去獲取鎖的時候,會直接去嘗試獲取,獲取不到,再去進入等待隊列,如果能獲取到,就直接獲取到鎖。

上面說過在ReentrantLock中默認使用的非公平鎖,兩種方式:

  1. ReentrantLock fairSyncLock = new ReentrantLock(false); 

或者:

  1. ReentrantLock fairSyncLock = new ReentrantLock(); 

都可以實現非公平鎖。

「優點」

可以減少CPU喚醒線程的開銷,整體的吞吐效率會高點,CPU也不必取喚醒所有線程,會減少喚起線程的數量。

「缺點」

大家可能也發現了,這樣可能導致隊列中間的線程一直獲取不到鎖或者長時間獲取不到鎖,導致餓死。

獨享鎖和共享鎖

獨享鎖

獨享鎖也叫排他鎖/互斥鎖,是指該鎖一次只能被一個線程鎖持有。如果線程T對數據A加上排他鎖后,則其他線程不能再對A加任何類型的鎖。獲得排他鎖的線程既能讀數據又能修改數據。JDK中的synchronized和JUC中Lock的實現類就是互斥鎖。

共享鎖

共享鎖是指該鎖可被多個線程所持有。如果線程T對數據A加上共享鎖后,則其他線程只能對A再加共享鎖,不能加排他鎖。獲得共享鎖的線程只能讀數據,不能修改數據。

對于ReentrantLock而言,其是獨享鎖。但是對于Lock的另一個實現類ReadWriteLock,其讀鎖是共享鎖,其寫鎖是獨享鎖。

  1. 讀鎖的共享鎖可保證并發讀是非常高效的,讀寫,寫讀 ,寫寫的過程是互斥的。
  2. 獨享鎖與共享鎖也是通過AQS來實現的,通過實現不同的方法,來實現獨享或者共享。

可重入鎖

若當前線程執行中已經獲取了鎖,如果再次獲取該鎖時,就會獲取不到被阻塞。

  1. public class RentrantLockDemo { 
  2.     public synchronized void test(){ 
  3.         System.out.println("test"); 
  4.     } 
  5.  
  6.     public synchronized void test1(){ 
  7.         System.out.println("test1"); 
  8.         test(); 
  9.     } 
  10.  
  11.     public static void main(String[] args) { 
  12.         RentrantLockDemo rentrantLockDemo = new RentrantLockDemo(); 
  13.         //線程1 
  14.         new Thread(() -> rentrantLockDemo.test1()).start(); 
  15.     } 

當一個線程執行test1()方法的時候,需要獲取rentrantLockDemo的對象鎖,在test1方法匯總又會調用test方法,但是test()的調用是需要獲取對象鎖的。

可重入鎖也叫「遞歸鎖」,指的是同一線程外層函數獲得鎖之后,內層遞歸函數仍然有獲取該鎖的代碼,但不受影響。

ThreadLocal設計原理

ThreadLocal名字中有個Thread表示線程,Local表示本地,我們就理解為線程本地變量了。

先看看ThreadLocal的整體:

最關心的三個公有方法:set、get、remove。

構造方法

  1. public ThreadLocal() { 

構造方法里沒有任何邏輯處理,就是簡單的創建一個實例。

set方法

源碼為:

  1. public void set(T value) { 
  2.     //獲取當前線程     
  3.     Thread t = Thread.currentThread(); 
  4.     //這是什么鬼?     
  5.     ThreadLocalMap map = getMap(t);         
  6.     if (map != null)             
  7.         map.set(this, value);        
  8.     else 
  9.         createMap(t, value); 

先看看ThreadLocalMap是個什么東東:

ThreadLocalMap是ThreadLocal的靜態內部類。

set方法整體為:

ThreadLocalMap構造方法:

  1. //這個屬性是ThreadLocal的,就是獲取hashcode(這列很有學問,但是我們的目的不是他) 
  2. private final int threadLocalHashCode = nextHashCode(); 
  3. private Entry[] table
  4. private static final int INITIAL_CAPACITY = 16; 
  5. //Entry是一個弱引用         
  6. static class Entry extends WeakReference<ThreadLocal<?>> { 
  7.     Object value; 
  8.     Entry(ThreadLocal<?> k, Object v) { 
  9.         super(k); 
  10.         value = v;    
  11.     }  
  12.  
  13. ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) { 
  14.     //數組默認大小為16 
  15.     table = new Entry[INITIAL_CAPACITY]; 
  16.     //len 為2的n次方,以ThreadLocal的計算的哈希值按照Entry[]取模(為了更好的散列) 
  17.     int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1); 
  18.     table[i] = new Entry(firstKey, firstValue); 
  19.     size = 1; 
  20.     //設置閾值(擴容閾值) 
  21.     setThreshold(INITIAL_CAPACITY);   

然后我們看看map.set()方法中是如何處理的:

  1. private void set(ThreadLocal<?> key, Object value) { 
  2. Entry[] tab = table
  3.          int len = tab.length; 
  4.          //len 為2的n次方,以ThreadLocal的計算的哈希值按照Entry[]取模 
  5.          int i = key.threadLocalHashCode & (len-1); 
  6.          //找到ThreadLocal對應的存儲的下標,如果當前槽內Entry不為空, 
  7.          //即當前線程已經有ThreadLocal已經使用過Entry[i] 
  8.          for (Entry e = tab[i]; 
  9.               e != null
  10.               e = tab[i = nextIndex(i, len)]) { 
  11.              ThreadLocal<?> k = e.get(); 
  12.               // 當前占據該槽的就是當前的ThreadLocal ,更新value結束 
  13.              if (k == key) { 
  14.                  e.value = value; 
  15.                  return
  16.              } 
  17.              //當前卡槽的弱引用可能會回收了,key:null value:xxxObject , 
  18.              //需清理Entry原來的value ,便于垃圾回收value,且將新的value 放在該槽里,結束 
  19.              if (k == null) { 
  20.                  replaceStaleEntry(key, value, i); 
  21.                  return
  22.              } 
  23.          } 
  24.         //在這之前沒有ThreadLocal使用Entry[i],并進行值存儲 
  25.          tab[i] = new Entry(key, value); 
  26.          //累計Entry所占的個數 
  27.          int sz = ++size
  28.          // 清理key 為null 的Entry ,可能需要擴容,擴容長度為原來的2倍,并需要進行重新hash 
  29.          if (!cleanSomeSlots(i, sz) && sz >= threshold){ 
  30.              rehash(); 
  31.          } 

從上面這個set方法,我們就大致可以把這三個進行一個關聯了:

Thread、ThreadLocal、ThreadLocalMap。

get方法

remove方法

expungeStaleEntry方法代碼里有點大,所以這里就貼了出來。

  1. //刪除陳舊entry的核心方法 
  2. private int expungeStaleEntry(int staleSlot) { 
  3.     Entry[] tab = table
  4.     int len = tab.length;             
  5.     tab[staleSlot].value = null;//刪除value 
  6.     tab[staleSlot] = null;//刪除entry 
  7.     size--;//map的size自減 
  8.     // 遍歷指定刪除節點,所有后續節點 
  9.     Entry e; 
  10.     int i; 
  11.     for (i = nextIndex(staleSlot, len); 
  12.          (e = tab[i]) != null
  13.          i = nextIndex(i, len)) { 
  14.         ThreadLocal<?> k = e.get(); 
  15.         if (k == null) {//keynull,執行刪除操作 
  16.             e.value = null
  17.             tab[i] = null
  18.             size--; 
  19.         } else {//key不為null,重新計算下標 
  20.             int h = k.threadLocalHashCode & (len - 1); 
  21.             if (h != i) {//如果不在同一個位置 
  22.                 tab[i] = null;//把老位置的entry置null(刪除) 
  23.                 // 從h開始往后遍歷,一直到找到空為止,插入                          
  24.                 while (tab[h] != null){ 
  25.                     h = nextIndex(h, len); 
  26.                 } 
  27.                 tab[h] = e;    
  28.             } 
  29.         } 
  30.     } 
  31.     return i; 

本文轉載自微信公眾號「Java后端技術全?!梗梢酝ㄟ^以下二維碼關注。轉載本文請聯系Java后端技術全棧公眾號。

 

責任編輯:武曉燕 來源: Java后端技術全棧
相關推薦

2023-04-02 17:53:10

多線程編程自測

2010-01-18 14:09:58

C++多線程

2024-10-21 16:59:37

C#編程多線程

2022-10-20 18:00:00

MyBatis緩存類型

2024-02-01 14:59:14

多線程硬件系統

2024-06-04 07:52:04

2020-10-29 07:06:39

Python多線程多核

2020-09-21 10:50:24

Java多線程代碼

2020-11-16 07:22:32

騰訊多線程

2024-11-04 09:39:08

Java?接口Thread?類

2009-03-12 10:52:43

Java線程多線程

2018-04-03 09:27:42

分布式架構系統

2024-04-17 09:52:00

操作系統多線程內存

2013-07-16 10:12:14

iOS多線程多線程概念多線程入門

2023-06-05 07:56:10

線程分配處理器

2023-06-06 08:17:52

多線程編程Thread類

2010-01-21 11:27:30

linux多線程機制線程同步

2021-12-26 18:22:30

Java線程多線程

2009-06-29 17:49:47

Java多線程

2021-03-11 00:07:30

線程Thread程序
點贊
收藏

51CTO技術棧公眾號

av激情在线观看| www.午夜av| www.中文字幕久久久| 国产a亚洲精品| 综合久久综合久久| 国产精品成人一区二区三区| 欧美不卡视频在线观看| 精品国产精品| 日韩欧美不卡在线观看视频| 人妻有码中文字幕| 日p在线观看| 99久久精品免费看国产| 国产精品99一区| 久久久99精品| 第一会所sis001亚洲| 亚洲精品一区二区三区精华液| 黑森林福利视频导航| 巨大荫蒂视频欧美大片| 久久精品视频在线看| 欧美激情性做爰免费视频| 国产精品无码在线| 国产天堂在线播放视频| 久久久99免费| 国产精品福利视频| 亚洲中文字幕在线观看| 在线一区免费观看| 超碰精品一区二区三区乱码| 美女100%无挡| 国产成人高清精品免费5388| 69精品人人人人| 99精品视频网站| 人成免费电影一二三区在线观看| 国产精品影音先锋| 国产精品一区二区三区免费视频| 国产精品免费av一区二区| 91精品国产福利在线观看麻豆| 亚洲欧美日韩精品久久亚洲区 | 日日夜夜免费精品视频| 久久久久久欧美| 超碰手机在线观看| 国产精品88久久久久久| 中文字幕欧美日韩| 亚洲综合欧美综合| 欧美一级在线| 色婷婷综合中文久久一本| 99在线观看视频免费| 好了av在线| 国产精品久久久99| 亚洲天堂电影网| 91在线导航| 激情亚洲综合在线| 国产精品极品在线| 全网免费在线播放视频入口| 成人在线一区| 日韩在线免费视频| 午夜激情视频在线播放| 91影院成人| 精品日韩99亚洲| 绯色av蜜臀vs少妇| 超碰97久久| 精品国产乱码久久久久久免费| 992tv人人草| 日韩精品中文字幕吗一区二区| 91精品久久久久久久99蜜桃| 一区二区三区国产好的精华液| 在线播放成人| 337p亚洲精品色噜噜狠狠| 中文字幕第22页| 亚洲国产中文在线二区三区免| 欧美一级二级三级蜜桃| 波多野结衣办公室双飞| 欧美日韩一区二区三区四区不卡| 日韩高清不卡av| 天天爽夜夜爽一区二区三区| 成人在线免费电影网站| 欧美精品精品一区| 黑人无套内谢中国美女| 老牛精品亚洲成av人片| 欧美日本不卡视频| 特级西西444www| 国内精品麻豆美女在线播放视频| 亚洲毛茸茸少妇高潮呻吟| 一级肉体全黄裸片| 影视一区二区| 亚州av一区二区| 波多野结衣电车痴汉| 精品一区二区免费在线观看| 国产精品免费区二区三区观看| 午夜性色福利视频| 国产乱码精品一区二区三| 国产91一区二区三区| 日韩专区一区二区| 国产精品国产三级国产三级人妇| 国产精品一二三在线观看| 欧美日韩在线观看首页| 欧美日韩黄色影视| 国产精品成人无码专区| 精品免费视频| 久久久久久久久久久亚洲| 中文字幕在线天堂| 国产毛片精品视频| 日本视频一区二区在线观看| 亚洲欧美日本在线观看| 中文字幕免费观看一区| 欧美 亚洲 视频| 香蕉视频亚洲一级| 亚洲精品在线观看视频| 91社区视频在线观看| 亚洲第一精品影视| 成人黄色av免费在线观看| 色欲久久久天天天综合网| 国产精品美女久久久久久久久| 999一区二区三区| 精品九九久久| 精品在线观看国产| 高h视频免费观看| 另类小说视频一区二区| 你懂的视频在线一区二区| 色综合999| 欧美二区三区91| 最近中文字幕在线mv视频在线| 欧美日韩免费| 成人免费在线网址| 国产精品影院在线| 懂色av中文一区二区三区天美 | wwwxxx亚洲| 国产乱码字幕精品高清av | 欧美在线观看一二区| 四季av综合网站| 欧美一区影院| 成人精品福利视频| 在线观看精品一区二区三区| 色综合久久天天综合网| 国产草草浮力影院| 韩国亚洲精品| 91免费版网站在线观看| 日本高清视频在线播放| 欧美在线观看视频在线| 日本高清www| 亚洲中字黄色| 久久亚洲精品欧美| 日本在线影院| 亚洲国产欧美一区二区三区同亚洲 | 久久视频在线看| 中文字幕男人天堂| 欧美国产成人精品| 精品999在线| 欧美一区电影| 国产精品流白浆视频| 国产高清视频在线观看| 在线观看视频一区| 国产精品国产三级国产专业不| 久久九九精品| 日本成人看片网址| 高清成人在线| 亚洲香蕉在线观看| 成人小视频在线播放| 欧美国产综合色视频| 亚洲一区二区三区四区五区xx| 欧美日韩播放| 国产精品久久久久久久午夜| a视频网址在线观看| 欧美美女喷水视频| 国产成人免费在线观看视频| 韩国v欧美v日本v亚洲v| av动漫在线播放| 中文字幕一区二区三区中文字幕| 精品自在线视频| 亚洲奶汁xxxx哺乳期| 午夜伊人狠狠久久| 色欲av无码一区二区三区| 日韩黄色小视频| 一区二区三区三区在线| 免费观看在线一区二区三区| 久久久人成影片一区二区三区观看| 黄色三级网站在线观看| 日韩欧美在线免费观看| 污污视频网站在线免费观看| 国产美女一区二区| 精品国产一区三区| 青青草97国产精品麻豆| 91在线短视频| 欧美大片免费高清观看| 久久精品国产综合| 天堂在线观看免费视频| 欧美亚洲一区二区在线观看| 午夜69成人做爰视频| 91免费在线视频观看| 在线观看免费视频高清游戏推荐| 欧美日韩爆操| 日韩欧美三级电影| 亚洲精品不卡在线观看| 日韩免费黄色av| a视频在线免费看| 精品亚洲精品福利线在观看| 一本大道伊人av久久综合| 亚洲午夜免费视频| av免费播放网站| av亚洲精华国产精华精| 三上悠亚在线一区| 国产欧美日韩综合一区在线播放| 一区二区三区偷拍| 亚洲人成网www| 97人人干人人| 国产福利一区二区三区在线播放| 欧美激情欧美狂野欧美精品| h视频网站在线观看| 亚洲精品久久久久久久久久久| 一起草av在线| 色噜噜久久综合| 中文字幕一区二区三区乱码不卡| 久久精品首页| 国产v片免费观看| 亚洲欧洲日韩| 午夜精品区一区二区三| 亚洲aa在线| 国产伦精品一区二区三区高清版| 国产亚洲欧美日韩精品一区二区三区| 91国在线精品国内播放 | 91精品国产高清自在线看超| 含羞草www国产在线视频| 中文字幕日韩视频| 日本天堂在线| 亚洲电影免费观看高清完整版在线观看| 在线视频1卡二卡三卡| 欧美体内谢she精2性欧美| 欧美一级高潮片| 亚洲精品v日韩精品| 国产精品99久久久精品无码| 欧美一级久久| 成人国产在线看| 亚洲成人一区| 亚洲第一精品区| 日韩在线观看| 亚洲国产日韩欧美| 精品理论电影在线| 日韩久久久久久久| 国产精品一区二区av交换| 精品一区二区视频| 久久99久久99精品免观看软件| 欧美精品videos另类日本| fc2ppv国产精品久久| 欧美成人免费视频| 天天综合网在线| 精品国精品国产| 午夜美女福利视频| 日韩欧美中文字幕一区| av男人天堂av| 日韩视频在线你懂得| 精品国产黄色片| 日韩一区二区在线播放| 国内老熟妇对白xxxxhd| 欧美成人精精品一区二区频| 精品人妻一区二区三区麻豆91| 日韩你懂的在线观看| 亚洲成人av综合| 亚洲精品在线一区二区| 午夜福利视频一区二区| 日韩精品在线视频| 国产视频福利在线| 中文字幕在线视频日韩| 色综合久久影院| 九九精品在线播放| 岛国av在线播放| 国产a级全部精品| 成人久久网站| 91美女高潮出水| 97一区二区国产好的精华液| 国产一区二区三区高清视频| 西野翔中文久久精品字幕| 日本在线观看一区二区三区| 久久中文亚洲字幕| 国产亚洲精品久久久久久久| 国产欧美一区二区三区国产幕精品| 国产二区视频在线播放| 欧美久久成人| 久草热视频在线观看| 丝袜美腿亚洲色图| 国产大片一区二区三区| av不卡免费电影| 国产精品无码无卡无需播放器| 亚洲欧洲综合另类| 中日韩黄色大片| 欧美日韩高清影院| 五月天婷婷在线播放| 一个色综合导航| 男女免费观看在线爽爽爽视频| 色婷婷综合成人| 欧美男男video| 国产国语刺激对白av不卡| 国产高清精品二区| 美脚丝袜一区二区三区在线观看| 日韩中文欧美| av网站大全免费| 欧美三级乱码| 男女无套免费视频网站动漫| 国产美女主播视频一区| 大又大又粗又硬又爽少妇毛片| 亚洲欧美国产高清| 在线观看亚洲网站| 欧美日韩中文字幕| 精品国产乱码一区二区三| 亚洲色图国产精品| 欧美日韩色网| 91精品久久久久久久久久入口| 成人午夜大片| 欧美 日韩 国产 在线观看| 国产欧美一级| 熟女人妻一区二区三区免费看| 国产欧美精品一区二区色综合 | 欧美影视一区二区三区| 亚洲风情第一页| 日韩三级成人av网| 日韩精品专区| 精品一区在线播放| 黄色成人在线网址| 久久精品一卡二卡| 欧美激情一二三区| 欧美黄色一级大片| 亚洲国产欧美自拍| 午夜影院免费在线| 成人日韩在线电影| 日韩美女一区二区三区在线观看| 播放灌醉水嫩大学生国内精品| 国产91精品在线观看| 亚洲色偷偷综合亚洲av伊人| 欧美日韩中字一区| 黄网站在线观看| 热99精品里视频精品| 国产精品网在线观看| 国产情侣第一页| 国产福利一区在线| 人妻av一区二区| 一区二区三区欧美视频| 99热这里精品| 成年人精品视频| 久久久久亚洲精品中文字幕| 中文字幕欧美日韩一区二区| 日本不卡的三区四区五区| 亚洲精品国产精品国自产网站| 欧美日韩中文在线| 精品视频二区| 国产成人免费91av在线| 国产伦精品一区二区三区视频 | 精品小视频在线观看| 日韩一级免费观看| 日本动漫理论片在线观看网站 | 97香蕉超级碰碰久久免费软件| 国产精品1luya在线播放| 日韩av在线播放不卡| 成人黄色网址在线观看| 国产一级免费av| 亚洲精品ady| 九色porny自拍视频在线观看| 精品一区二区三区自拍图片区| 亚洲欧美成人综合| 在线观看日本中文字幕| 在线一区二区三区| 四虎久久免费| 91精品黄色| 亚洲国产高清一区| 黑人巨大精品欧美| 精品视频在线免费| 成人在线视频亚洲| 国产精品我不卡| 男人的天堂亚洲在线| 美国美女黄色片| 欧美第一区第二区| 涩涩涩视频在线观看| 亚洲a∨一区二区三区| 国内精品国产三级国产a久久| 黄色在线观看免费| 日韩精品视频免费| 久久av日韩| 可以看毛片的网址| 国产视频一区不卡| 国产美女精品视频国产| 国内精品在线一区| 成人久久久久| 日本少妇xxx| 日韩欧美高清在线视频| 无遮挡的视频在线观看| 国产99视频精品免费视频36| 视频一区在线播放| 极品久久久久久| 精品夜色国产国偷在线| 国产亚洲亚洲国产一二区| heyzo亚洲| 中文字幕亚洲综合久久菠萝蜜| 免费av网站观看| 国产精品入口免费视频一| 欧美体内she精视频在线观看| 少妇按摩一区二区三区| 欧美三级日韩在线| www成人免费观看| 在线观看免费黄色片| 91浏览器在线视频| 精品国产区一区二| 国产高清在线不卡| 日韩图片一区|