自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

京東二面:為什么Netty要?jiǎng)?chuàng)造FastThreadLocal?

開發(fā) 前端
FastThreadLocal?FastThreadLocal 運(yùn)行快的原因是啥?除了快之外,它還有其他優(yōu)勢嗎?ThreadLocal 線程本地變量,每個(gè)線程都擁有一份該變量的獨(dú)立副本,即使是在多線程環(huán)境下,每個(gè)線程也只能修改和訪問自己的那份副本,從而避免了線程安全問題,實(shí)現(xiàn)了線程間的隔離。

FastThreadLocal 從字面意義上來看,它是“Fast”+“ThreadLocal”的結(jié)合體,寓意為快速的 ThreadLocal。那么,問題來了,Netty 為什么要再造一個(gè) FastThreadLocal?FastThreadLocal 運(yùn)行快的原因是啥?除了快之外,它還有其他優(yōu)勢嗎?

1.先從ThreadLocal說起

ThreadLocal 線程本地變量,每個(gè)線程都擁有一份該變量的獨(dú)立副本,即使是在多線程環(huán)境下,每個(gè)線程也只能修改和訪問自己的那份副本,從而避免了線程安全問題,實(shí)現(xiàn)了線程間的隔離。

ThreadLocal 底層是使用 ThreadLocalMap 實(shí)現(xiàn)的,這點(diǎn)從 JDK 的源碼中可以看出,核心源碼如下:

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

從 ThreadLocal 的 set 方法可以看出,ThreadLocal 是存儲在 ThreadLocalMap 中的,咱們繼續(xù)看 ThreadLocalMap 的源碼實(shí)現(xiàn):

從上面源碼可以看出,ThreadLocalMap 中存放的是 Entry(哈希桶),而 Entry 中的 key 就是 ThreadLocal,而 value 則是要存儲的值,所以我們得出 ThreadLocal 的底層實(shí)現(xiàn)如下:

2.ThreadLocal存在的問題

(1)性能問題

因?yàn)?ThreadLocal 底層是使用 ThreadLocalMap 實(shí)現(xiàn)的,ThreadLocalMap 類似于哈希表。當(dāng)一個(gè)線程是擁有多個(gè) ThreadLocal 時(shí),ThreadLocalMap 很容易發(fā)生 Hash 沖突,此時(shí) ThreadLocal 就不得不使用線性探測法來解決哈希沖突了,而在解決 Hash 沖突時(shí)需要不停地向下尋找,效率較低,因此 ThreadLocal 存在的第一個(gè)問題就是性能較低。

(2)內(nèi)存泄漏問題

ThreadLocal 也存在內(nèi)存泄漏的問題,具體來說 ThreadLocalMap 使用 ThreadLocal 對象作為鍵(Key),并且這個(gè)鍵是弱引用(WeakReference)類型。這意味著當(dāng)沒有其他強(qiáng)引用指向 ThreadLocal 對象時(shí),它將會在下次垃圾回收時(shí)被回收。然而,Entry 中保存的值(Value)仍然是強(qiáng)引用,這就可能導(dǎo)致以下問題:

  • 弱引用鍵的回收:一旦外部對 ThreadLocal 實(shí)例的所有強(qiáng)引用消失,ThreadLocal 對象本身就會變?yōu)槿蹩蛇_(dá)狀態(tài)。在下一次垃圾回收時(shí),由于是弱引用,ThreadLocal 對象會被回收,但 Entry 中的 Value(即實(shí)際存儲的數(shù)據(jù))仍然是強(qiáng)引用,因此不會被回收。
  • Map 引用陷阱:即使 ThreadLocal 鍵被回收,Entry 仍然存在于 ThreadLocalMap 中,并且由于 Map 對 Entry 的引用,這些 Entry 所持有的 Value 對象也不會被垃圾回收,從而導(dǎo)致這些對象無法被使用也無法被釋放,形成了所謂的“內(nèi)存泄漏”。
  • 線程長期存活:在一些場景下,特別是使用線程池時(shí),線程的生命周期往往很長,甚至伴隨整個(gè)應(yīng)用的生命周期。這意味著 ThreadLocalMap 中的 Entry 可能會長時(shí)間不被清理,進(jìn)一步加劇了內(nèi)存泄漏問題。

所以,綜合來看,在使用 ThreadLocal 時(shí),如果在使用完之后,未及時(shí)調(diào)用 remove() 方法的話,就會出現(xiàn)內(nèi)存泄漏的問題。

3.FastThreadLocal特點(diǎn)

為了解決 ThreadLocal 存在的這些問題,所以 Netty 創(chuàng)造出了一個(gè) FastThreadLocal,F(xiàn)astThreadLocal 的特點(diǎn)如下。

(1)效率高

FastThreadLocal 之所以性能高的原因是因?yàn)槠浯鎯Y(jié)構(gòu),在 FastThreadLocal 中并沒有向 ThreadLocal 那樣,使用哈希表來存儲元素,而是使用了數(shù)組來進(jìn)行元素存儲,它的核心實(shí)現(xiàn)源碼如下:

public class FastThreadLocal<V> {
    // FastThreadLocal中的index是記錄了該它維護(hù)的數(shù)據(jù)應(yīng)該存儲的位置
    // InternalThreadLocalMap數(shù)組中的下標(biāo), 它是在構(gòu)造函數(shù)中確定的
    private final int index;
 
    public InternalThreadLocal() {
        index = InternalThreadLocalMap.nextVariableIndex();
    }
    // 省略其他代碼
}

FastThreadLocal 核心類 InternalThreadLocalMap 的實(shí)現(xiàn)源碼如下:

public final class InternalThreadLocalMap extends UnpaddedInternalThreadLocalMap {
    // 自增索引, ?于計(jì)算下次存儲到Object數(shù)組中的位置
    private static final AtomicInteger nextIndex = new AtomicInteger();
 
    private static final int ARRAY_LIST_CAPACITY_MAX_SIZE = Integer.MAX_VALUE - 8;
 
    public static int nextVariableIndex() {
        int index = nextIndex.getAndIncrement();
        if (index >= ARRAY_LIST_CAPACITY_MAX_SIZE || index < 0) {
            nextIndex.set(ARRAY_LIST_CAPACITY_MAX_SIZE);
            throw new IllegalStateException("too many thread-local indexed variables");
        }
        return index;
    }
    // 省略其他代碼
}

從上述源碼可以看出,F(xiàn)astThreadLocal 在初始化的時(shí)候分配一個(gè)數(shù)組索引 index,index 的值采用原子類 AtomicInteger 保證順序遞增,通過調(diào)用 InternalThreadLocalMap.nextVariableIndex() 方法獲得。然后在讀寫數(shù)據(jù)的時(shí)候通過數(shù)組下標(biāo) index 直接定位到 FastThreadLocal 的位置,時(shí)間復(fù)雜度為 O(1)。如果數(shù)組下標(biāo)遞增到非常大,那么數(shù)組也會比較大,所以 FastThreadLocal 是通過空間換時(shí)間的思想提升讀寫性能。

因此,在 FastThreadLocal 中并不需要使用線性探測法來解決 Hash 沖突,因?yàn)樗鞘褂脭?shù)組進(jìn)行存儲的,每次使用下標(biāo)進(jìn)行查詢即可,它的查詢時(shí)間復(fù)雜度也是 O(1) 的,所以它的操作效率很高。

(2)安全性更高

JDK 原生的 ThreadLocal 使用不當(dāng)可能造成內(nèi)存泄漏,只能等待線程銷毀。然而 FastThreadLocal 卻不存在這個(gè)問題,在 FastThreadLocal 中不僅提供了 remove() 方法可以主動清除對象,而且它還封裝了 FastThreadLocalRunnable,F(xiàn)astThreadLocalRunnable 在最后使用完之后會自動調(diào)用 removeAll() 方法將集合中所有對象清理掉,因此 FastThreadLocal 更安全。

FastThreadLocalRunnable 自動清除對象的實(shí)現(xiàn)核心源碼如下:

final class FastThreadLocalRunnable implements Runnable {
    private final Runnable runnable;
    @Override
    public void run() {
        try {
            runnable.run();
        } finally {
            FastThreadLocal.removeAll();
        }
    }
    static Runnable wrap(Runnable runnable) {
        return runnable instanceof FastThreadLocalRunnable 
                ? runnable : new FastThreadLocalRunnable(runnable);
    }
}

4.小結(jié)

FastThreadLocal 相比于 ThreadLocal 存在以下兩個(gè)主要優(yōu)點(diǎn):

  • 性能更高:FastThreadLocal 使用了數(shù)組的方式來存儲元素,所以它的查詢時(shí)間復(fù)雜度 O(1) 相比于 ThreadLocal 的哈希表操作效率更高。
  • 安全性更高:FastThreadLocal 中的 FastThreadLocalRunnable 在最后執(zhí)行完之后會自動調(diào)用 removeAll() 將集合中所有對象都清理掉,可以避免內(nèi)存泄漏的問題,所以它的安全性更高。
責(zé)任編輯:姜華 來源: Java中文社群
相關(guān)推薦

2021-10-27 20:54:24

分庫分表高并發(fā)

2023-10-19 11:12:15

Netty代碼

2009-09-29 16:24:11

2022-10-18 08:38:16

內(nèi)存泄漏線程

2025-04-14 10:00:00

負(fù)載均衡Java開發(fā)

2025-01-14 08:32:55

2015-08-06 10:14:15

造輪子facebook

2022-08-15 08:27:02

基站網(wǎng)絡(luò)

2013-03-12 14:30:09

Ubuntu操作系統(tǒng)

2019-12-18 09:42:19

技術(shù) Linux網(wǎng)絡(luò)

2024-04-03 09:01:34

SpringTomcat容器

2024-03-26 07:58:12

Redis編程模型

2021-04-25 09:58:48

mmapJava面試

2024-04-19 08:05:26

鎖升級Java虛擬機(jī)

2017-09-08 08:35:16

Android代碼API設(shè)計(jì)

2024-10-17 16:41:57

KafkaZooKeeper

2014-08-25 10:00:18

開源

2015-05-12 11:04:42

Java EE學(xué)習(xí)Java EE

2019-03-19 08:59:13

物聯(lián)網(wǎng)IOT技術(shù)

2019-11-27 10:25:15

SaaS云端IT架構(gòu)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號