ThreadLocal的介绍与使用
本篇是《圖解Java多線程設計模式》第十一章的讀書筆記。
有一個儲物室,里面有很多儲物柜。每個人拿著自己的鑰匙去開自己的儲物柜,雖然進同一個儲物室,但彼此互不干擾。這就是 Thread-Local Storage 線程中的局部存儲空間。
來看一個例子:每個線程將信息打印到自己的日志文件中。
TSLog :打印類,每個線程都有自己的 TSLog 對象,向自己的日志文件中打印信息。
public class TSLog {private PrintWriter writer;public TSLog(String filename) {try {writer = new PrintWriter(new FileWriter(filename));} catch (IOException e) {e.printStackTrace();}}public void println(String s) {writer.println(s);}public void close() {System.out.println("=====End of log======");writer.close();} }Log:利用 ThreadLocal 獲取請求對應的 TSLog 對象。
public class Log {private static final ThreadLocal<TSLog> tsLogCollection = new ThreadLocal<>();private static TSLog getTSLog() {TSLog tsLog = tsLogCollection.get();if (tsLog == null) {tsLog = new TSLog(Thread.currentThread().getName()+"-log.txt");tsLogCollection.set(tsLog);}return tsLog;}public static void println(String s) {getTSLog().println(s);}public static void close() {getTSLog().close();} }ClientThread:請求線程
public class ClientThread extends Thread {public ClientThread(String name) {super(name);}@Overridepublic void run() {System.out.println(getName()+" BEGIN");for (int i = 0; i < 10; i++) {Log.println("i = "+i);try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}}Log.close();System.out.println(getName()+" END");} }Main:測試
public class Main {public static void main(String[] args) {new ClientThread("Zhao").start();new ClientThread("Qian").start();new ClientThread("Sun").start();} }類的關系圖:
ThreadLocal 是線程特有的存儲空間的意思,也就是只屬于該線程,各個線程各自專用。
雖然 Thread-local storage 模式沒有使用到鎖,但這并不意味著吞吐量的提高,因為 ThreadLocal 存儲/獲取操作會產生額外的性能開銷,該模式的重點在于沒有顯示的執行互斥處理,導致編程時犯錯的可能性減小。
由于 ThreadLocal 會自動判斷當前的線程,這相當于在程序中引入了上下文,這有一定危險,因為我們看不到處理中所用到的信息。例如在找Bug時,我們關注信息的流向,但上下文的引入導致當前Bug的產生可能是程序之前的行為導致的,也就導致定位問題的難度。
基于角色與基于任務
關于線程與線程使用的信息之間的關系,由基于角色和基于任務兩種思考方式。
主體與客體
要想組裝塑料模型,以下二者缺一不可。
- 組裝塑料模型的人
- 塑料模型套件
同理假設我們需要讓線程去完成一項工作,那么以下二者缺一不可:
- 工作的線程
- 工作所需的信息
總結以下就是:
- 用于做某事的主體
- 用于做某事的客體
在設計多線程程序時,根據以 主體 為主還是以 客體 為主的不同產生了以下兩種方式
- 基于角色:以主體為主
- 基于任務:以客體為主
基于角色的考慮方式
即”線程最偉大"的方式。線程的實例來保存進行工作所必須的信息(上下文,狀態)。這樣減少了線程之間的交互信息量。一個線程使用從其它線程接收到的信息來執行處理(指的是線程間的通信),改變自己的內部狀態。我們將這種線程稱為角色。用代碼來表示大致如下:
class Actor extends Thread {// 角色的內部狀態public void run() {// 循環的從外部接收并執行任務,改變內部狀態} }該書的第12章介紹的 Active Object 模式就是一種角色。
基于任務的考慮方式
即“任務最偉大”的方式。信息保存在線程之間交互的實例中。不僅是數據還有請求的方法都在該實例中。我們稱該實例為信息,請求或是命令。這里暫稱為任務,任務儲存了足夠的信息,可以說這是一種 富任務 往來于 輕線程之間的方式。典型就是書的第8章介紹的 Worker Thread 模式。
用代碼表示大致如下:
java.util.TimerTask 類就是一個基于任務的類。該類實現了 Runnable 接口,它會被 java.util.Timer 類調用。如果要定義一項在一定時間后進行的工作或是頂起進行的工作,可以使用該類。
java.util.concurrent.FutureTask 類也是一個基于任務的類。
實際上兩種方式是綜合在一起的
我們通常采用的是 角色之間通過任務交互 的形式。
總結
以上是生活随笔為你收集整理的ThreadLocal的介绍与使用的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 基于眼电信号(EOG)的眨眼检测算法
- 下一篇: RRU BBU