JAVA高逼格面試:執行緒封閉

  • 2020 年 1 月 14 日
  • 筆記

原創:小姐姐味道(微信公眾號ID:xjjdog),歡迎分享,轉載請保留出處。

碼農的世界從來不缺乏名詞。如果沒有,我們就強行弄上幾個。這些名詞有垂直領域的知識縮寫,也有水平領域的抽象劃分。有的行雲流水無比順暢,有的晦澀難懂如便秘。

在java的並發編程里,就有一個比較晦澀的名詞,叫做執行緒封閉。在以往的技術交流中,經常有人提到這個東西。那它到底是何方神聖,又有什麼用的功效呢?

你去搜索一下網路上的文章,都會給你三個選項。

1)Ad-hoc執行緒封閉。

2)棧封閉。

3)ThreadLocal類。

這些知識,戳破了那層窗戶紙,內容其實並不複雜。可怕的是這些名詞,為了記住它們真是蛋碎了一地。

意義?

我們都知道,一個變數如果被多個執行緒所使用,勢必會引入同步問題。除了同步關鍵字,java引入了多種技術來達到多執行緒的同步問題,包括wait、notify,可重入Lock,AQS等。這種編程方式會增加程式的複雜性,使得程式碼容易發生bug。

如果有一些數據,僅僅和執行緒有關,對執行緒外的數據是不可見的,那程式碼寫起來就美好的多。實現了這種效果的技術,就統一稱為執行緒封閉(thread confinement)。

這是前提。接下來我們來看實現。

棧封閉

棧封閉屬於強行湊概念的一個範疇,它對寫程式碼的人其實是不可見的,它是JVM里虛擬機棧或者本地方法棧的默認行為。其實,我們早就知道這個結果:成員變數是執行緒共享的,而局部變數是執行緒相關的。

很簡單的道理,但背後的原理需要深入了解JVM。為了了解這個功能,我們需要對JVM的記憶體區域劃分有一個初步的了解。

JVM除了存儲空間最大的堆,還有執行緒相關的,正在運行的棧。棧封閉指的就是與執行緒相關的棧的相關行為。

我們稍微回憶一下上圖中的記憶體劃分,棧封閉指的就是圖中彩色部分與執行緒相關的記憶體區域。

我們也可以再往下深挖一下。

虛擬機棧上的基本數據,其實是一種稱作棧幀的東西。你可以把棧幀理解成某個方法的執行。

在每個方法壓棧後,其中存在局部變數表、操作數棧、動態連接、返回地址等資訊。我們的局部變數,其實就是存在與這些地方。由於它們的祖先,最終只會指向一個執行緒,所以它們的作用範圍就被封閉了。

如上圖所示,局部變數和某個執行緒的關係。java中還有個執行緒執行的記憶體模型JMM,不過那是對變數的複製和同步,說的不是一回事。

ThreadLocal

其實,java提供給開發者唯一的執行緒封閉API,就是ThreadLocal。

Thread類中,有一個成員變數threadLocals,存放了與本執行緒相關的所有自定義資訊。對這個變數的定義在Thread,而操作卻在ThreadLocal類中。

public T get() {          Thread t = Thread.currentThread();          ThreadLocalMap map = getMap(t);          ...  }  ThreadLocalMap getMap(Thread t) {          return t.threadLocals;  }

關於ThreadLocal的用法有很多,比如常用的,每個執行緒生成一個執行緒不安全的SimpleDateFormat

ThreadLocal<SimpleDateFormat> sdf = new ThreadLocal<SimpleDateFormat>();  sdf.set(new SimpleDateFormat());

關於ThreadLocal不做過多介紹,直接查看jdk源程式碼即可獲取所有加成。

Ad-hoc

這些資訊大多來源於《JAVA並發編程》一書。我特地搜索了一下Ad-hoc這個名字的意思。

Ad-hoc模式就和以前的直連雙絞線概念一樣,是P2P的連接,所以無法與其它網路中的節點進行溝通,減少了干擾。 英文中作為形容詞有「特別的」,「臨時」的含義。

實在是無法理解為什麼要用到這樣的命名。

這種方式,完全靠實現者控制,所以非常脆弱。

好吧,看來還是老老實實用ThreadLocal好了。

End

我們看一下這三種方式的歸宿。其中一種是JVM內部實現的,原理方面的知識;Ad-hoc是告訴用戶這種執行緒封閉式很困難的,趕緊放棄;到最後,我們的手裡就只剩下了ThreadLocal了。

我彷彿看到了ThreadLocal在勝利的招手,同時我的名詞字典里又多了幾個:執行緒封閉、棧封閉、Ad-hoc。

作者簡介:小姐姐味道 (xjjdog),一個不允許程式設計師走彎路的公眾號。聚焦基礎架構和Linux。十年架構,日百億流量,與你探討高並發世界,給你不一樣的味道。