Java中執行緒區域性變數ThreadLocal使用教程及原始碼分析
在Java多執行緒程式設計中有時候會遇見執行緒本地區域性變數ThreadLocal這個類,下面就來講講ThreadLocal的使用及原始碼分析。
ThreadLocal 是Thread Local Varial(執行緒區域性變數)的意思,每個執行緒在使用執行緒區域性變數的時候都會為使用這個執行緒區域性變數的執行緒提供一個執行緒區域性變數的副本,使得每個執行緒都可以完全獨立地操作這個執行緒區域性變數,而不會與其他執行緒發生衝突,從執行緒的角度來看,每個執行緒都好像獨立地擁有了這個執行緒區域性變數。這樣,看似每個執行緒都在併發訪問同一個資源(執行緒區域性變數ThreadLocal),但每個執行緒都是各自獨立地操作這個執行緒變數,而與其它執行緒沒有任何關係,執行緒之間對執行緒區域性變數的操作都沒有任何干擾。而多執行緒同步機制是為了保證在任何一個時刻只有一個執行緒可以對共享資源進行修改。即ThreadLocal是為了隔離對資源的共享,而同步機制是為了同步多個執行緒對同一個共享資源的訪問,是多個執行緒之間的通訊方式。
ThreadLocal主要有以下幾個方法:
T ThreadLocal.get():返回此執行緒區域性變數中當前執行緒的副本值。
ThreadLocal.set(T value):給執行緒區域性變數中當前執行緒的副本中設定相應的值。
ThreadLocal.remove():刪除當前執行緒區域性變數中的值。
下面就給一個ThreadLocal的測試例程,可以幫助大家理解。
<span style="font-size:18px;"><span style="font-size:18px;">import java.util.HashMap; /** * 檔名稱:ThreadLocal測試例程ThreadLocalTest * 時間:2016 5-23 17:35 * 說明:1、同步機制是為了同步多個執行緒對相同資源的併發訪問,是多個執行緒之間進行通訊的有效方式;而ThreadLocal是為了隔離多個 * 執行緒的資料共享,隔離多個執行緒之間的共享衝突。 * */ public class ThreadLocalTest { private static HashMap<Integer,Integer> mMap=new HashMap<Integer,Integer>(); private static ThreadLocal<Integer> mThreadLocal=new ThreadLocal<Integer>(); static class FirstThread extends Thread { int n; int i=0; public FirstThread() { super(); // TODO Auto-generated constructor stub } public FirstThread(String name) { super(name); // TODO Auto-generated constructor stub } @Override public void run() { if(mMap.get(0)!=null){ n=mMap.get(0); } for(i=0;i<5;i++){ n++; } mMap.put(0,n); System.out.println(currentThread().getName()+",value="+mMap.get(0)); } } static class SecondThread extends Thread { int n=0; int i=0; public SecondThread() { super(); // TODO Auto-generated constructor stub } public SecondThread(String name) { // TODO Auto-generated constructor stub super(name); } @Override public void run() { if(mThreadLocal.get()!=null){ n=mThreadLocal.get(); } for(i=0;i<5;i++){ n++; } mThreadLocal.set(n); System.out.println(currentThread().getName()+",value="+mThreadLocal.get()); } } /** * @param args */ public static void main(String[] args) { int i=0; for(i=0;i<5;i++){ new FirstThread("FirstThread"+i).start(); } for(i=0;i<5;i++){ new SecondThread("SecondThread"+i).start(); } } } </span></span>
執行結果如下:
可以發現FirstThread是對同一個共享資源mMap訪問,多個執行緒對mMap.get(0)中的值進行修改導致每個執行緒執行後讀取到mMap.get(0)各不一樣。而SecondThread由於使用了ThreadLocal這樣,每個執行緒中都有一個ThreadLocal副本,就像每個執行緒各自擁有一個ThreadLocal變數一樣,這樣SecondThread執行後讀取到的資料都是一樣的。
下面來分析一下ThreadLocal原始碼,我們以Android4.3中的ThreadLocal原始碼來分析。首先來看下ThreadLocal的結構:
觀察發現ThreadLocal裡面主要get、set、remove等常見方法及一些屬性變數,並含有一個靜態的內部類Values,
<span style="font-size:18px;">/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package java.lang;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Implements a thread-local storage, that is, a variable for which each thread
* has its own value. All threads share the same {@code 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
* {@code null} values.
*
* @see java.lang.Thread
* @author Bob Lee
* ThreadLocal<T>是一個泛型類,表示執行緒區域性變數,泛型T是要儲存資料的型別,將TreadLocal型別為T的資料
* 按照key-value鍵值對的形式存入到Values物件中
*
*/
public class ThreadLocal<T> {
/* Thanks to Josh Bloch and Doug Lea for code reviews and impl advice. */
/**
* Creates a new thread-local variable.
*/
public ThreadLocal() {}
/**
* 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 = values(currentThread);
if (values != null) {
Object[] table = values.table;
/*獲取key的索引值*/
int index = hash & values.mask;
/*如果鍵值的key的索引為index,則所對應到的value索引為index+1.*/
if (this.reference == table[index]) {
return (T) table[index + 1];
}
} else {
/*如果當前Value例項為空,則建立一個Value例項*/
values = initializeValues(currentThread);
}
return (T) values.getAfterMiss(this);
}
/**
* Provides the initial value of this variable for the current thread.
* The default implementation returns {@code null}.
*
* @return the initial value of the variable.返回當前執行緒執行緒區域性變數ThreadLocal的初始值
*/
protected T initialValue() {
return null;
}
/**
* 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);
}
/**
* Removes the entry for this variable in the current thread. If this call
* is followed by a {@link #get()} before a {@link #set},
* {@code #get()} will call {@link #initialValue()} and create a new
* entry with the resulting value.
*
* @since 1.5
*/
public void remove() {
Thread currentThread = Thread.currentThread();
Values values = values(currentThread);
if (values != null) {
values.remove(this);
}
}
/**
* Creates Values instance for this thread and variable type.
* 給當前執行緒建立要給Values例項物件
*/
Values initializeValues(Thread current) {
return current.localValues = new Values();
}
/**
* Gets Values instance for this thread and variable type.
* 獲取當前執行緒的的Values例項物件
*/
Values values(Thread current) {
return current.localValues;
}
/** Weak reference to this thread local instance. */
private final Reference<ThreadLocal<T>> reference
= new WeakReference<ThreadLocal<T>>(this);
/** Hash counter. */
private static AtomicInteger hashCounter = new AtomicInteger(0);
/**
* Internal hash. We deliberately don't bother with #hashCode().
* Hashes must be even. This ensures that the result of
* (hash & (table.length - 1)) points to a key and not a value.
*
* We increment by Doug Lea's Magic Number(TM) (*2 since keys are in
* every other bucket) to help prevent clustering.
*/
private final int hash = hashCounter.getAndAdd(0x61c88647 * 2);
/**
* Per-thread map of ThreadLocal instances to values.
*/
static class Values {
/**
* Size must always be a power of 2.
* 預設的初始化容量.
*/
private static final int INITIAL_SIZE = 16;
/**
* Placeholder for deleted entries.
* 表示被刪除的實體標記物件,移除變數時它是把對應的key的位置賦值為TOMBSTONE
*/
private static final Object TOMBSTONE = new Object();
/**
* Map entries. Contains alternating keys (ThreadLocal) and values.
* The length is always a power of 2.
* Table陣列,儲存變數的地方,長度必須是2的n次方的值.
*/
private Object[] table;
/** Used to turn hashes into indices.
* 計算下標的掩碼,它的值是table的長度-1
* */
private int mask;
/** Number of live entries.
* 存放進來的實體的數量
* */
private int size;
/** Number of tombstones.
* 表示被刪除的實體的數量
* */
private int tombstones;
/** Maximum number of live entries and tombstones.
* 閾值,最大負載因子,用來判斷是否需要進行rehash
* */
private int maximumLoad;
/** Points to the next cell to clean up.
* 表示下一個要進行清理的位置點
* */
private int clean;
/**
* Constructs a new, empty instance.
*/
Values() {
initializeTable(INITIAL_SIZE);
this.size = 0;
this.tombstones = 0;
}
/**
* Used for InheritableThreadLocals.
*/
Values(Values fromParent) {
this.table = fromParent.table.clone();
this.mask = fromParent.mask;
this.size = fromParent.size;
this.tombstones = fromParent.tombstones;
this.maximumLoad = fromParent.maximumLoad;
this.clean = fromParent.clean;
inheritValues(fromParent);
}
/**
* Inherits values from a parent thread.
*/
@SuppressWarnings({"unchecked"})
private void inheritValues(Values fromParent) {
// Transfer values from parent to child thread.
Object[] table = this.table;
for (int i = table.length - 2; i >= 0; i -= 2) {
Object k = table[i];
if (k == null || k == TOMBSTONE) {
// Skip this entry.
continue;
}
// The table can only contain null, tombstones and references.
Reference<InheritableThreadLocal<?>> reference
= (Reference<InheritableThreadLocal<?>>) k;
// Raw type enables us to pass in an Object below.
InheritableThreadLocal key = reference.get();
if (key != null) {
// Replace value with filtered value.
// We should just let exceptions bubble out and tank
// the thread creation
table[i + 1] = key.childValue(fromParent.table[i + 1]);
} else {
// The key was reclaimed.
table[i] = TOMBSTONE;
table[i + 1] = null;
fromParent.table[i] = TOMBSTONE;
fromParent.table[i + 1] = null;
tombstones++;
fromParent.tombstones++;
size--;
fromParent.size--;
}
}
}
/**
* Creates a new, empty table with the given capacity.
* 根據給定的容量建立要給Table
*/
private void initializeTable(int capacity) {
/*通過capacity建立table容器*/
this.table = new Object[capacity * 2];
/*下標的掩碼*/
this.mask = table.length - 1;
/*下一個要進行清理的位置點初始化為0*/
this.clean = 0;
/*最大負載因子*/
this.maximumLoad = capacity * 2 / 3; // 2/3
}
/**
* Cleans up after garbage-collected thread locals.
* 在ThreadLocal物件被垃圾回收之後進行清理
*/
private void cleanUp() {
if (rehash()) {
// If we rehashed, we needn't clean up (clean up happens as
// a side effect).
return;
}
if (size == 0) {
// No live entries == nothing to clean.
return;
}
// Clean log(table.length) entries picking up where we left off
// last time.
int index = clean;
Object[] table = this.table;
for (int counter = table.length; counter > 0; counter >>= 1,
index = next(index)) {
Object k = table[index];
/*如果已經刪除,則刪除下一個*/
if (k == TOMBSTONE || k == null) {
continue; // on to next entry
}
// The table can only contain null, tombstones and references.
@SuppressWarnings("unchecked")
Reference<ThreadLocal<?>> reference
= (Reference<ThreadLocal<?>>) k;/*將從Table陣列中獲取到的物件進行強轉*/
/*如果ThreadLocal物件已經不存在,則釋放value資料*/
if (reference.get() == null) {
// This thread local was reclaimed by the garbage collector.
table[index] = TOMBSTONE;
table[index + 1] = null;
tombstones++;
size--;
}
}
// Point cursor to next index.
clean = index;
}
/**
* Rehashes the table, expanding or contracting it as necessary.
* Gets rid of tombstones. Returns true if a rehash occurred.
* We must rehash every time we fill a null slot; we depend on the
* presence of null slots to end searches (otherwise, we'll infinitely
* loop).
*/
private boolean rehash() {
if (tombstones + size < maximumLoad) {
return false;
}
int capacity = table.length >> 1;
// Default to the same capacity. This will create a table of the
// same size and move over the live entries, analogous to a
// garbage collection. This should only happen if you churn a
// bunch of thread local garbage (removing and reinserting
// the same thread locals over and over will overwrite tombstones
// and not fill up the table).
int newCapacity = capacity;
if (size > (capacity >> 1)) {
// More than 1/2 filled w/ live entries.
// Double size.
newCapacity = capacity * 2;
}
Object[] oldTable = this.table;
// Allocate new table.
initializeTable(newCapacity);
// We won't have any tombstones after this.
this.tombstones = 0;
// If we have no live entries, we can quit here.
if (size == 0) {
return true;
}
// Move over entries.
for (int i = oldTable.length - 2; i >= 0; i -= 2) {
Object k = oldTable[i];
if (k == null || k == TOMBSTONE) {
// Skip this entry.
continue;
}
// The table can only contain null, tombstones and references.
@SuppressWarnings("unchecked")
Reference<ThreadLocal<?>> reference
= (Reference<ThreadLocal<?>>) k;
ThreadLocal<?> key = reference.get();
if (key != null) {
// Entry is still live. Move it over.
add(key, oldTable[i + 1]);
} else {
// The key was reclaimed.
size--;
}
}
return true;
}
/**
* Adds an entry during rehashing. Compared to put(), this method
* doesn't have to clean up, check for existing entries, account for
* tombstones, etc.
*/
void add(ThreadLocal<?> key, Object value) {
for (int index = key.hash & mask;; index = next(index)) {
Object k = table[index];
if (k == null) {
/*把ThreadLocal物件(key)和對應的value放在連續的位置中*/
table[index] = key.reference;
table[index + 1] = value;
return;
}
}
}
/**
* Sets entry for given ThreadLocal to given value, creating an
* entry if necessary.
*/
void put(ThreadLocal<?> key, Object value) {
cleanUp();/*清理廢棄的元素*/
// Keep track of first tombstone. That's where we want to go back
// and add an entry if necessary.
int firstTombstone = -1;
for (int index = key.hash & mask;; index = next(index)) {
Object k = table[index];
/*如果TheadLocal物件是存在,存入value物件*/
if (k == key.reference) {
// Replace existing entry.
table[index + 1] = value;
return;
}
if (k == null) {
if (firstTombstone == -1) {
// Fill in null slot.
table[index] = key.reference;
table[index + 1] = value;
size++;
return;
}
// Go back and replace first tombstone.
table[firstTombstone] = key.reference;
table[firstTombstone + 1] = value;
tombstones--;
size++;
return;
}
// Remember first tombstone.
if (firstTombstone == -1 && k == TOMBSTONE) {
firstTombstone = index;
}
}
}
/**
* Gets value for given ThreadLocal after not finding it in the first
* slot.
*/
Object getAfterMiss(ThreadLocal<?> key) {
Object[] table = this.table;
int index = key.hash & mask;
// If the first slot is empty, the search is over.
if (table[index] == null) {
Object value = key.initialValue();
// If the table is still the same and the slot is still empty...
if (this.table == table && table[index] == null) {
table[index] = key.reference;
table[index + 1] = value;
size++;
cleanUp();
return value;
}
// The table changed during initialValue().
put(key, value);
return value;
}
// Keep track of first tombstone. That's where we want to go back
// and add an entry if necessary.
int firstTombstone = -1;
// Continue search.
for (index = next(index);; index = next(index)) {
Object reference = table[index];
if (reference == key.reference) {
return table[index + 1];
}
// If no entry was found...
if (reference == null) {
Object value = key.initialValue();
// If the table is still the same...
if (this.table == table) {
// If we passed a tombstone and that slot still
// contains a tombstone...
if (firstTombstone > -1
&& table[firstTombstone] == TOMBSTONE) {
table[firstTombstone] = key.reference;
table[firstTombstone + 1] = value;
tombstones--;
size++;
// No need to clean up here. We aren't filling
// in a null slot.
return value;
}
// If this slot is still empty...
if (table[index] == null) {
table[index] = key.reference;
table[index + 1] = value;
size++;
cleanUp();
return value;
}
}
// The table changed during initialValue().
put(key, value);
return value;
}
if (firstTombstone == -1 && reference == TOMBSTONE) {
// Keep track of this tombstone so we can overwrite it.
firstTombstone = index;
}
}
}
/**
* Removes entry for the given ThreadLocal.
*/
void remove(ThreadLocal<?> key) {
cleanUp();
for (int index = key.hash & mask;; index = next(index)) {
Object reference = table[index];
if (reference == key.reference) {
// Success!
table[index] = TOMBSTONE;
table[index + 1] = null;
tombstones++;
size--;
return;
}
if (reference == null) {
// No entry found.
return;
}
}
}
/**
* Gets the next index. If we're at the end of the table, we wrap back
* around to 0.
*/
private int next(int index) {
return (index + 2) & mask;
}
}
}
</span>
首先看下ThreadLocal類裡面幾個屬性,
<span style="font-size:18px;"> /** Weak reference to this thread local instance. */
private final Reference<ThreadLocal<T>> reference
= new WeakReference<ThreadLocal<T>>(this);
</span>
使用弱引用來儲存ThreadLocal物件本身,對於只使用弱引用的物件而言,當系統執行垃圾回收機制時不管系統記憶體是否足夠,總會回收該物件所佔記憶體。
<span style="font-size:18px;"><pre name="code" class="java"> /** Hash counter. */
private static AtomicInteger hashCounter = new AtomicInteger(0);
/**
* Internal hash. We deliberately don't bother with #hashCode().
* Hashes must be even. This ensures that the result of
* (hash & (table.length - 1)) points to a key and not a value.
*
* We increment by Doug Lea's Magic Number(TM) (*2 since keys are in
* every other bucket) to help prevent clustering.
*/
private final int hash = hashCounter.getAndAdd(0x61c88647 * 2);</span>
hashCounter物件是可以進行加、減等安全操作的整數。getAndSet(int newValue)取當前的值,並設定新的值;而0x61c88647 * 2作用是:在Value存在的資料主要儲存陣列table上,而table被設計為下標為0,2,4…2n的位置存放key,而1,3,5…(2n +1 )的位置存放value,`0x61c88647 * 2保證了其二進位制中最低位為0,也就是在計算key的下標時,一定是偶數位。
下面來看下ThreadLocal.get方法的內部實現:
<span style="font-size:18px;"> /**
* 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 = values(currentThread);
if (values != null) {
Object[] table = values.table;
/*獲取key的索引值*/
int index = hash & values.mask;
/*如果鍵值的key的索引為index,則所對應到的value索引為index+1.*/
if (this.reference == table[index]) {
return (T) table[index + 1];
}
} else {
/*如果當前Value例項為空,則建立一個Value例項*/
values = initializeValues(currentThread);
}
return (T) values.getAfterMiss(this);
}</span>
在ThreadLocal.get方法中首先獲取ThreadLocal執行緒區域性變數所對應的當前執行緒,即得到ThreadLocal執行緒區域性變數所線上程,然後呼叫ThreadLocal.values(Thread current)方法得到當前執行緒對應的ThreadLocal物件,由前面的講解可知道ThreadLocal變數是與每個執行緒對應的,進入ThreadLocal.values(Thread current)可以發現如下程式碼:
<span style="font-size:18px;"> /**
* Gets Values instance for this thread and variable type.
* 獲取當前執行緒的的Values例項物件
*/
Values values(Thread current) {
return current.localValues;
}</span>
而current.localValues是Thread類中的一個屬性,進一步追蹤在Thread類中可以發現
<span style="font-size:18px;"> /**
* Normal thread local values.
*/
ThreadLocal.Values localValues;</span>
因此這樣每個執行緒與一個ThreadLocal物件一一物件,當通過ThreadLocal.values(Thread current)方法得到的values物件時如果為空呼叫ThreadLocal.initializeValues(Thread current)方法來初始化一個values物件,如果獲取到的values物件不為空則取出型別為泛型T的資料。如果獲取到的Values型別的values物件不為空的話,拿到values.table物件陣列,然後按照一定取出values.table陣列中的值,注意在Values型別的物件中含有一個table物件陣列,該物件陣列中0、2、4……等偶數下標位置儲存key鍵,1、3、5……等奇數下標位置儲存value值,通過這種方式來儲存key-value鍵值對,關於ThreadLocal中靜態內部類Values的內部實現後面會有一定的分析,Values型別資料就是用來儲存key-value的鍵值對。
既然看完ThreadLocal.get()方法中的內部實現,接下來就看看ThreadLocal.set(T value)內部實現,
/**
* 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);
}
和get方法類似,set方法首先拿到當前執行緒,然後再拿到當前執行緒的Values型別物件values,如果values為空則呼叫ThreadLocal.initializeValues(Thread current)對values進行初始化,最後再向values中存入資料。
下面來分析一下Values內部類的實現,Values幾個重要的屬性如下:
/**
* Size must always be a power of 2.
* 預設的初始化容量.
*/
private static final int INITIAL_SIZE = 16;
/**
* Placeholder for deleted entries.
* 表示被刪除的實體標記物件,移除變數時它是把對應的key的位置賦值為TOMBSTONE
*/
private static final Object TOMBSTONE = new Object();
/**
* Map entries. Contains alternating keys (ThreadLocal) and values.
* The length is always a power of 2.
* Table陣列,儲存變數的地方,長度必須是2的n次方的值.
*/
private Object[] table;
/** Used to turn hashes into indices.
* 計算下標的掩碼,它的值是table的長度-1
* */
private int mask;
/** Number of live entries.
* 存放進來的實體的數量
* */
private int size;
/** Number of tombstones.
* 表示被刪除的實體的數量
* */
private int tombstones;
/** Maximum number of live entries and tombstones.
* 閾值,最大負載因子,用來判斷是否需要進行rehash
* */
private int maximumLoad;
/** Points to the next cell to clean up.
* 表示下一個要進行清理的位置點
* */
private int clean;
Values的建構函式如下:
/**
* Constructs a new, empty instance.
*/
Values() {
initializeTable(INITIAL_SIZE);
this.size = 0;
this.tombstones = 0;
}
/**
* Used for InheritableThreadLocals.
*/
Values(Values fromParent) {
this.table = fromParent.table.clone();
this.mask = fromParent.mask;
this.size = fromParent.size;
this.tombstones = fromParent.tombstones;
this.maximumLoad = fromParent.maximumLoad;
this.clean = fromParent.clean;
inheritValues(fromParent);
}
對於Values中的方法我們以ThreadLocal.Values.put(ThreadLocal<?> key, Object value)方法為例進行分析,
/**
* Sets entry for given ThreadLocal to given value, creating an
* entry if necessary.
*/
void put(ThreadLocal<?> key, Object value) {
cleanUp();/*清理廢棄的元素*/
// Keep track of first tombstone. That's where we want to go back
// and add an entry if necessary.
int firstTombstone = -1;
for (int index = key.hash & mask;; index = next(index)) {
Object k = table[index];
/*如果TheadLocal物件是存在,存入value物件*/
if (k == key.reference) {
// Replace existing entry.
table[index + 1] = value;
return;
}
if (k == null) {
if (firstTombstone == -1) {
// Fill in null slot.
table[index] = key.reference;
table[index + 1] = value;
size++;
return;
}
// Go back and replace first tombstone.
table[firstTombstone] = key.reference;
table[firstTombstone + 1] = value;
tombstones--;
size++;
return;
}
// Remember first tombstone.
if (firstTombstone == -1 && k == TOMBSTONE) {
firstTombstone = index;
}
}
}
可以發現通過Values物件儲存key-value鍵值對是放在一個一維的Object[]陣列中,偶數下標位儲存key,奇數下標位儲存value。ThreadLocal.Values.getAfterMiss(ThreadLocal<?> key)等方法亦是如此。