国产片侵犯亲女视频播放_亚洲精品二区_在线免费国产视频_欧美精品一区二区三区在线_少妇久久久_在线观看av不卡

服務(wù)器之家:專注于服務(wù)器技術(shù)及軟件下載分享
分類導(dǎo)航

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

服務(wù)器之家 - 編程語言 - Java教程 - 詳解Java中ThreadLocal類型及簡(jiǎn)單用法

詳解Java中ThreadLocal類型及簡(jiǎn)單用法

2022-02-19 14:42Mr.Ymx Java教程

ThreadLocal實(shí)例通常是希望將狀態(tài)與線程關(guān)聯(lián)起來的類中的私有靜態(tài)字段,下面通過例子給大家詳細(xì)介紹Java中ThreadLocal類型及簡(jiǎn)單用法,感興趣的朋友跟隨小編一起看看吧

1 基本概念

ThreadLocal類提供了線程局部變量。這些變量與普通變量的不同之處在于,每個(gè)訪問一個(gè)變量(通過其get或set方法)的線程都有自己的、獨(dú)立初始化的變量副本。ThreadLocal實(shí)例通常是希望將狀態(tài)與線程關(guān)聯(lián)起來的類中的私有靜態(tài)字段(例如,用戶ID或事務(wù)ID)。

例如,下面的類生成每個(gè)線程本地的唯一標(biāo)識(shí)符。 線程的id在第一次調(diào)用ThreadId.get()時(shí)被賦值,并且在后續(xù)調(diào)用中保持不變。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class ThreadId {
    // 包含要分配的下一個(gè)線程ID的原子整數(shù)
    private static final AtomicInteger nextId = new AtomicInteger(0);
 
    // 包含每個(gè)線程ID的線程局部變量
    private static final ThreadLocal<Integer> threadId = new ThreadLocal<Integer>() {
        @Override
        protected Integer initialValue() {
            return nextId.getAndIncrement();
        }
    };
 
    // 返回當(dāng)前線程的唯一ID,并在必要時(shí)賦值
    public static int get() {
        return threadId.get();
    }
}

只要線程是活的并且ThreadLocal實(shí)例是可訪問的,每個(gè)線程都持有一個(gè)對(duì)線程局部變量副本的隱式引用; 當(dāng)一個(gè)線程離開后,它的所有線程本地實(shí)例副本都將被垃圾收集(除非存在對(duì)這些副本的其他引用)

2 簡(jiǎn)單使用

?
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
private static ThreadLocal<String> threadLocal = new ThreadLocal();
 
private static void print(String thread) {
    //打印當(dāng)前線程中本地內(nèi)存中本地變量的值
    System.out.println(thread + " :" + threadLocal.get());
    //清除本地內(nèi)存中的本地變量
    threadLocal.remove();
}
 
public static void main(String[] args) {
    new Thread(() -> {
        //設(shè)置線程1副本變量的值
        threadLocal.set("I am Thread1");
        //調(diào)用打印方法
        print("thread1");
        //打印本地變量
        System.out.println("after remove : " + threadLocal.get());
    }).start();
 
    new Thread(() -> {
        //設(shè)置線程2副本變量的值
        threadLocal.set("I am Thread2");
        //調(diào)用打印方法
        print("thread2");
        System.out.println("after remove : " + threadLocal.get());
    }).start();
}

運(yùn)行結(jié)果:

thread1 :I am Thread1
thread2 :I am Thread2
after remove : null
after remove : null

由上邊的程序可以看出,ThreadLocal就是將本地變量在多線程訪問條件下給每個(gè)線程一個(gè)副本變量,圖示:

詳解Java中ThreadLocal類型及簡(jiǎn)單用法

3 應(yīng)用場(chǎng)景

最典型應(yīng)用場(chǎng)景就是Spring的聲明式事務(wù)、 解決數(shù)據(jù)庫連接、Session管理

那數(shù)據(jù)庫鏈接為例:

將數(shù)據(jù)庫的鏈接示例在每個(gè)線程中都有一份副本數(shù)據(jù),在某一個(gè)線程關(guān)閉連接的時(shí)候就不會(huì)關(guān)閉其他鏈接,session同理。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
private static final ThreadLocal<Connection> connection = new ThreadLocal() {
    @Override
    protected Object initialValue() {
        return Connection.getConnection();
    }
};
 
private static Connection getConnections() {
    return connection.get();
}
 
public static void main(String[] args) {
    new Thread(() -> {
        Connection connection = getConnections();
        //不會(huì)關(guān)閉其他鏈接
        connection.close();
    }).start();
    new Thread(() -> {
        Connection connection = getConnections();
        connection.close();
    }).start();
}

4 底層原理

ThreadLocal類型主要有3個(gè)方法和一個(gè)數(shù)據(jù)結(jié)構(gòu),分別是get()、set(Object)、remove()及ThreadLocalMap

4.1 set(Object)

將該線程局部變量的當(dāng)前線程副本設(shè)置為指定的值。 大多數(shù)子類都不需要重寫這個(gè)方法,只依賴于initialValue方法來設(shè)置線程局部變量的值。

參數(shù): Value -要存儲(chǔ)在當(dāng)前線程本地線程的副本中的值。

?
1
2
3
4
5
6
7
8
9
10
11
12
public void set(T value) {
    //獲取調(diào)用者線程
    Thread t = Thread.currentThread();
    //以當(dāng)前線程作為key值,去查找對(duì)應(yīng)的線程變量,找到對(duì)應(yīng)的map
    ThreadLocalMap map = getMap(t);
    //如果map不為null,就直接添加本地變量,key為當(dāng)前定義的ThreadLocal變量的this引用,值為添加的本地變量值
    if (map != null)
        map.set(this, value);
    //如果map為null,說明首次添加,需要首先創(chuàng)建出對(duì)應(yīng)的map
    else
        createMap(t, value);
}

4.2 get()

返回該線程局部變量的當(dāng)前線程副本中的值。 如果變量在當(dāng)前線程中沒有值,則首先將其初始化為initialValue方法調(diào)用所返回的值。

返回: 這個(gè)線程本地的當(dāng)前線程值

?
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
public T get() {
    Thread t = Thread.currentThread();
    //獲取當(dāng)前線程的threadLocals變量
    ThreadLocalMap map = getMap(t);
    //如果threadLocals變量不為null,就可以在map中查找到本地變量的值
    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }
    //執(zhí)行到此處,threadLocals為null,調(diào)用該更改初始化當(dāng)前線程的threadLocals變量
    return setInitialValue();
}
 
private T setInitialValue() {
    //protected T initialValue() {return null;}
    T value = initialValue();
    //獲取當(dāng)前線程
    Thread t = Thread.currentThread();
    //以當(dāng)前線程作為key值,去查找對(duì)應(yīng)的線程變量,找到對(duì)應(yīng)的map
    ThreadLocalMap map = getMap(t);
    //如果map不為null,就直接添加本地變量,key為當(dāng)前線程,值為添加的本地變量值
    if (map != null)
        map.set(this, value);
    //如果map為null,說明首次添加,需要首先創(chuàng)建出對(duì)應(yīng)的map
    else
        createMap(t, value);
    return value;
}

4.3 remove()

移除此線程局部變量的當(dāng)前線程值。如果這個(gè)線程局部變量隨后被當(dāng)前線程讀取,它的值將通過調(diào)用它的initialValue方法重新初始化,除非它的值是由當(dāng)前線程在中間設(shè)置的。這可能導(dǎo)致在當(dāng)前線程中多次調(diào)用initialValue方法。

?
1
2
3
4
5
6
7
public void remove() {
    //獲取當(dāng)前線程綁定的threadLocals
    ThreadLocalMap m = getMap(Thread.currentThread());
    //如果map不為null,就移除當(dāng)前線程中指定ThreadLocal實(shí)例的本地變量
    if (m != null)
        m.remove(this);
}

4.4 ThreadLocalMap

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
static class ThreadLocalMap {
    static class Entry extends WeakReference<ThreadLocal<?>> {
        Object value;
        Entry(ThreadLocal<?> k, Object v) {
            super(k);
            value = v;
        }
    }
    private static final int INITIAL_CAPACITY = 16;
    //有效Entry數(shù)組
    private Entry[] table;
    //大小
    private int size = 0;
    //負(fù)載因子
    private int threshold; // Default to 0
}

5 內(nèi)存泄漏隱患和防止策略

5.1 為什么會(huì)發(fā)生內(nèi)存泄漏?

在線程池中線程的存活時(shí)間太長,往往都是和程序同生共死的,這樣 Thread 持有的 ThreadLocalMap 一直都不會(huì)被回收,再加上 ThreadLocalMap 中的 Entry 對(duì) ThreadLocal 是弱引用(WeakReference),所以只要 ThreadLocal 結(jié)束了自己的生命周期是可以被回收掉的。
Entry 中的 Value 是被 Entry 強(qiáng)引用的,即便 value 的生命周期結(jié)束了,value 也是無法被回收的,導(dǎo)致內(nèi)存泄露。

5.2 怎樣防止內(nèi)存泄漏?

  • ThreadLocal申明為private static final xxx
  • ThreadLocal使用后務(wù)必調(diào)用remove方法。

參考文章:

https://www.cnblogs.com/fsmly/p/11020641.html

https://blog.csdn.net/meism5/article/details/90413860

https://blog.csdn.net/zzg1229059735/article/details/82715741

到此這篇關(guān)于詳解Java中ThreadLocal類型及簡(jiǎn)單用法的文章就介紹到這了,更多相關(guān)Java中ThreadLocal類型內(nèi)容請(qǐng)搜索服務(wù)器之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持服務(wù)器之家!

原文鏈接:https://blog.csdn.net/Mr_YanMingXin/article/details/120743280

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 国产99久久久精品视频 | 午夜精品久久久久久久男人的天堂 | 国产精品一区二区无线 | 最近免费观看高清韩国日本大全 | 欧美日韩视频一区二区 | 在线91| 亚洲xxxxx | 日韩精品一区二区三区在线观看 | 久草成人网| 国产精品九九九 | 欧美日韩精品一区二区在线播放 | 国产精品一区久久久久 | 亚洲欧美日韩另类精品一区二区三区 | 久久精品国产免费 | 国产视频在线看 | 亚洲专区中文字幕 | 久久高清片 | 中文在线一区 | 亚洲午夜精品视频 | 欧美一区三区 | 免费污片网站 | 欧美极品视频 | 免费观看一级一片 | 国产欧美自拍 | 都市激情国产 | 欧美一区二区三区在线 | 四虎网址 | 国产精品丝袜视频 | 亚洲电影在线播放 | 亚洲免费在线观看 | 水卜樱一区二区av | 日韩成人片 | 日韩精品在线免费观看 | 亚洲自拍偷拍精品 | 欧美日韩精品一区二区在线播放 | 精品在线播放 | 欧美日韩中文国产一区发布 | 欧美久久久久 | 成年人视频免费在线看 | 欧美一区二区在线视频 | 中文字幕亚洲欧美日韩在线不卡 |