一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - 快速了解Java中ThreadLocal類

快速了解Java中ThreadLocal類

2021-02-18 14:09mengwei Java教程

這篇文章主要介紹了快速了解Java中ThreadLocal類,介紹了ThreadLocal 是什么,ThreadLocal的作用,ThreadLocal 原理等相關內容,具有一定參考價值,需要的朋友可以了解下。

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

一、 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.

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

概括來講有三個特性:

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

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

test.java

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
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)共享同一個對象;然后,在不同的線程中修改對象的值和在不同的線程中訪問對象的值,并在控制臺輸出查看結果。

看結果:

快速了解Java中ThreadLocal類

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

二、 threadlocal的作用

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

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

statisticcosttime.java

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// 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;
    }

好了,工具設計完工了,現在我們用它來統計一下線程耗時試試唄:

main.java

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
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();
    }
}

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

快速了解Java中ThreadLocal類

看結果是不是和我們預想的不一樣,發現a的結果應該約等于b1+b2才對呀,怎么變成和b2一樣了那?答案就是我們在定義starttime和costtime變量時,本意是不應共享的,應是線程獨占的才對。而這里變量隨單例共享了,所以當計算a的值時,其實starttime已經被b2修改了,所以就輸出了和b2一樣的結果。

現在我們把statisticcosttime中注釋掉的部分打開,換成threadlocal的聲明方式試下。
看結果:

快速了解Java中ThreadLocal類

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

三、 threadlocal 原理

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

看源碼:

threadlocal 的成員變量set

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
 * 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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/**
 * 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

?
1
2
3
4
5
6
/**
 * creates values instance for this thread and variable type.
 */
values initializevalues(thread current) {
  return current.localvalues = new values();
}

threadlocal 的成員方法values

?
1
2
3
4
5
6
/**
 * gets values instance for this thread and variable type.
 */
values values(thread current) {
  return current.localvalues;
}

那這個values又是怎樣讀寫object那?

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

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

快速了解Java中ThreadLocal類

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

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

快速了解Java中ThreadLocal類

四、 總結

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

threadlocal最大的作用就是把線程共享變量轉換成線程本地變量,實現線程之間的隔離。

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

原文鏈接:https://www.2cto.com/kf/201608/541771.html

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 男人天堂bt | 爆操| 97久久免费视频 | 白丝女仆被啪到深夜漫画 | 亚洲成aⅴ人片在线 | 亚洲视频中文 | 成年视频在线观看 | 国产原创精品 | 99久久中文字幕伊人 | 动漫美女人物被黄漫在线看 | free极度另类性欧美 | 国产精品九九免费视频 | 猫咪社区免费资源在线观看 | 免费观看一级特黄三大片视频 | 午夜理论片日本中文在线 | 青青青青青操 | 湿好紧太硬了我太爽了 | 亚洲色图欧美色 | 69欧美另类xxxxx高清 | 欧美国产日韩1区俺去了 | 98色花堂永久地址国产精品 | 奇米影视亚洲狠狠色 | 国产精品亚洲w码日韩中文 国产精品香蕉在线观看不卡 | 毛片在线看网站 | 女同69式互添在线观看免费 | 亚洲国产精品久久卡一 | 校园肉文高h| 9420高清视频在线观看网百度 | 风间由美一区二区av101 | 国产在线观看人成激情视频 | www.99精品视频在线播放 | 亚洲国产cao| 国产精品性视频免费播放 | 99精品网站 | 四虎永久网址在线观看 | 2019午夜福合集高清完整版 | 男人最爱看的网站 | 国产精品合集一区二区 | 精品国产乱码久久久久久软件 | 欧美一级片免费 | 成人一区二区丝袜美腿 |