偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

ThreadLocal的使用及實現原理

開發(fā) 前端
ThreadLocal可以用來把實例變量共享成全局變量,讓程序中所有的方法都可以訪問到該變量。

前言

ThreadLocal直譯是本地線程,但實際上它的譯名是線程局部變量(ThreadLocalVariable)。ThreadLocal誕生的目的是隔離不同線程所使用的變量,官方對它的解釋是:

提供了線程局部變量,是獨立于變量的初始化副本”,也就是說它可以實現將某一個變量隔離在某個線程內,其它的線程無法訪問和使用這個變量。

我們先來做一個測試,先不使用ThreadLocal,創(chuàng)建三個線程:

public class ThreadLocalTest {
public static int num = 0;
public static int numAdd() {
return num++;
}
public static void main(String[] args) {
Thread t1 = new Thread(new MyRunnable());
Thread t2 = new Thread(new MyRunnable());
Thread t3 = new Thread(new MyRunnable());
t1.start();
t2.start();
t3.start();
}
public static class MyRunnable implements Runnable {
@Override
public void run() {
for (int i = 0; i < 3; i++) {
System.out.println(Thread.currentThread().getName() + "-" + ThreadLocalTest.numAdd());
}
}

}
}

執(zhí)行后發(fā)現控制臺輸出的是:

可以發(fā)現線程執(zhí)行了numAdd()方法,從0-8跑了九次,num從0加到8,也就是說線程之間共享了靜態(tài)變量,從而導致線程的不安全問題。

然后我們再使用ThreadLocal來進行測試。

public class ThreadLocalTest {
private static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>() {
@Override
protected Integer initialValue() {
return 0;
}
};
public static int numAdd() {
threadLocal.set(threadLocal.get()+1);
return threadLocal.get();
}
public static void main(String[] args) {
Thread t1 = new Thread(new MyRunnable());
Thread t2 = new Thread(new MyRunnable());
Thread t3 = new Thread(new MyRunnable());
t1.start();
t2.start();
t3.start();
}
public static class MyRunnable implements Runnable {
@Override
public void run() {
for (int i = 0; i < 3; i++) {
System.out.println(Thread.currentThread().getName() + "-" + numAdd());
}
}

}
}

這里的numAdd方法使用了ThreadLocal的get()方法,這個方法調用了initialValue()方法并設置了返回值為0,通過調用這個方法+1,達到了num++的效果,這時候再看輸出的結果。

可以看到,三個不同的線程間相互隔離,變量的取值互不相干,也就是說ThreadLocal使用了不相干的變量,或者說ThreadLocal為每一個線程準備了一個變量副本,那么它是如何實現的呢,我們點進ThreadLocal的源碼看看。

這就是ThreadLocal的構成了,主要操作是get()和set()方法:

get() : 返回當前ThreadLocal的值

 public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}

set() : 將當前線程對象的值存入ThreadLocalMap中

public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}

首先創(chuàng)建了當前Thread的對象,然后存入ThreadLocalMap中,對map進行判斷,不為空就將this(當前Threadlocal對象)存入作為key,并獲取對應的值,最后是調用了一個setInitialValue()方法去獲得初始化的值。

ThreadLocalMap

介紹上面兩個方法主要是是為了引出ThreadLocal的實現原理,即ThreadLocalMap的創(chuàng)建和使用。

官方注釋中解釋道,ThreadLocalMap是一個定制的哈希映射,只適用于維護ThreadLocal的值。在ThreadLocal類之外沒有導出操作。類是包私有的,以允許在類線程中聲明字段。為了幫助處理非常大且長期使用的用法,哈希表條目對鍵使用弱引用。

但是,由于不使用引用隊列,只有當表開始耗盡空間時,才開始刪除陳舊的條目。

點開ThreadLocalMap,可以看到一開始ThreadLocalMap定義了一個用于存儲數據的Entry 類。

 static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}

這個Entry類繼承了弱引用類,眾所周知Java有四種引用類型,其中弱引用就是每次JVM進行垃圾回收時,都會回收該對象,保證了ThreadLocal每次拷貝當前線程的值的時候所占的空間能被重新使用。

由get()方法可以得知,ThreadLocalMap的鍵(key)是ThreadLocal類的實例對象,value為用戶的值。

那么ThreadLocalMap的引用是在哪里呢,在上面的set()方法里,調用了getMap()和createMap()方法。

ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}

可以看到這邊調用了一個叫threadLocals的屬性,點擊這個屬性發(fā)現跳到了Thread類中。

  /* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;

所以這個屬性便是ThreadLocalMap的引用了,那么ThreadLocal的實現原理也就很清晰了:

  1. 定義了一個ThreadLocalMap內部類,使用的是Map的鍵值對方式來存取數據,key是ThreadLocal類的實例對象,value為傳值。
  2. 創(chuàng)建新的ThreadLocal對象,調用set()或get()方法時,也就是調用了ThreadLocalMap來進行操作。
  3. 使用ThreadLocal時,線程所使用的變量是獨享的(私有的變量副本),其他線程無法訪問,在使用過后(線程結束),這些變量會被GC回收。

使用ThreadLocal的原因

ThreadLocal可以用來把實例變量共享成全局變量,讓程序中所有的方法都可以訪問到該變量。

由于存到ThreadLocal的變量都是當前線程本身,其他線程無法訪問,存到ThreadLocal中只是為了方便在程序中同一個線程之間傳遞這個變量(和解決線程安全沒有關系)。

責任編輯:姜華 來源: 今日頭條
相關推薦

2021-05-06 08:55:24

ThreadLocal多線程多線程并發(fā)安全

2025-05-27 01:00:00

2015-03-10 13:55:31

JavaScript預解析原理及實現

2023-09-08 08:20:46

ThreadLoca多線程工具

2015-09-09 08:45:49

JavaThreadLocal

2009-03-27 10:10:13

c#遠程啟動遠程管理

2023-02-28 11:27:50

線程處理解決共享變量

2017-03-02 10:49:37

推薦算法原理實現

2017-02-06 19:26:15

iOSCFArray開源

2021-06-10 08:29:15

Rollup工具前端

2023-12-18 09:39:13

PreactHooks狀態(tài)管理

2015-12-02 14:10:56

HTTP網絡協(xié)議代理原理

2015-12-02 15:29:32

HTTP網絡協(xié)議代理原理

2023-08-31 08:12:23

應用場景業(yè)務異常HTTP

2018-07-27 08:39:44

負載均衡算法實現

2010-05-12 10:53:04

Symbian開發(fā)

2019-11-12 11:15:39

setTimeout前端代碼

2019-09-30 08:28:53

Delta LakeSpark數據原理

2009-09-22 17:13:53

Hibernate O

2017-07-04 12:26:14

ARARKit
點贊
收藏

51CTO技術棧公眾號