欧美一区二区三区老妇人-欧美做爰猛烈大尺度电-99久久夜色精品国产亚洲a-亚洲福利视频一区二区

ThreadLocal好不好用

這篇文章主要介紹“ThreadLocal好不好用”,在日常操作中,相信很多人在ThreadLocal好不好用問(wèn)題上存在疑惑,小編查閱了各式資料,整理出簡(jiǎn)單好用的操作方法,希望對(duì)大家解答”ThreadLocal好不好用”的疑惑有所幫助!接下來(lái),請(qǐng)跟著小編一起來(lái)學(xué)習(xí)吧!

站在用戶的角度思考問(wèn)題,與客戶深入溝通,找到崇明網(wǎng)站設(shè)計(jì)與崇明網(wǎng)站推廣的解決方案,憑借多年的經(jīng)驗(yàn),讓設(shè)計(jì)與互聯(lián)網(wǎng)技術(shù)結(jié)合,創(chuàng)造個(gè)性化、用戶體驗(yàn)好的作品,建站類(lèi)型包括:成都網(wǎng)站設(shè)計(jì)、網(wǎng)站制作、企業(yè)官網(wǎng)、英文網(wǎng)站、手機(jī)端網(wǎng)站、網(wǎng)站推廣、空間域名、網(wǎng)頁(yè)空間、企業(yè)郵箱。業(yè)務(wù)覆蓋崇明地區(qū)。

在 Java 中,如果要問(wèn)哪個(gè)類(lèi)使用簡(jiǎn)單,但用好最不簡(jiǎn)單?我想你的腦海中一定會(huì)浮現(xiàn)出一次詞——“ThreadLocal”。

確實(shí)如此,ThreadLocal  原本設(shè)計(jì)是為了解決并發(fā)時(shí),線程共享變量的問(wèn)題,但由于過(guò)度設(shè)計(jì),如弱引用和哈希碰撞,從而導(dǎo)致它的理解難度大和使用成本高等問(wèn)題。當(dāng)然,如果稍有不慎還是導(dǎo)致臟數(shù)據(jù)、內(nèi)存溢出、共享變量更新等問(wèn)題,但即便如此,ThreadLocal  依舊有適合自己的使用場(chǎng)景,以及無(wú)可取代的價(jià)值,比如本文要介紹了這兩種使用場(chǎng)景,除了 ThreadLocal 之外,還真沒(méi)有合適的替代方案。

使用場(chǎng)景1:本地變量

我們以多線程格式化時(shí)間為例,來(lái)演示 ThreadLocal 的價(jià)值和作用,當(dāng)我們?cè)诙鄠€(gè)線程中格式化時(shí)間時(shí),通常會(huì)這樣操作。

① 2個(gè)線程格式化

當(dāng)有 2 個(gè)線程進(jìn)行時(shí)間格式化時(shí),我們可以這樣寫(xiě):

import java.text.SimpleDateFormat; import java.util.Date;  public class Test {     public static void main(String[] args) throws InterruptedException {         // 創(chuàng)建并啟動(dòng)線程1         Thread t1 = new Thread(new Runnable() {             @Override             public void run() {                 // 得到時(shí)間對(duì)象                 Date date = new Date(1 * 1000);                 // 執(zhí)行時(shí)間格式化                 formatAndPrint(date);             }         });         t1.start();         // 創(chuàng)建并啟動(dòng)線程2         Thread t2 = new Thread(new Runnable() {             @Override             public void run() {                 // 得到時(shí)間對(duì)象                 Date date = new Date(2 * 1000);                 // 執(zhí)行時(shí)間格式化                 formatAndPrint(date);             }         });         t2.start();     }      /**      * 格式化并打印結(jié)果      * @param date 時(shí)間對(duì)象      */     private static void formatAndPrint(Date date) {         // 格式化時(shí)間對(duì)象         SimpleDateFormat simpleDateFormat = new SimpleDateFormat("mm:ss");         // 執(zhí)行格式化         String result = simpleDateFormat.format(date);         // 打印最終結(jié)果         System.out.println("時(shí)間:" + result);     } }

以上程序的執(zhí)行結(jié)果為:

ThreadLocal好不好用

上面的代碼因?yàn)閯?chuàng)建的線程數(shù)量并不多,所以我們可以給每個(gè)線程創(chuàng)建一個(gè)私有對(duì)象 SimpleDateFormat 來(lái)進(jìn)行時(shí)間格式化。

② 10個(gè)線程格式化

當(dāng)線程的數(shù)量從 2 個(gè)升級(jí)為 10 個(gè)時(shí),我們可以使用 for 循環(huán)來(lái)創(chuàng)建多個(gè)線程執(zhí)行時(shí)間格式化,具體實(shí)現(xiàn)代碼如下:

import java.text.SimpleDateFormat; import java.util.Date;  public class Test {     public static void main(String[] args) throws InterruptedException {         for (int i = 0; i < 10; i++) {             int finalI = i;             // 創(chuàng)建線程             Thread thread = new Thread(new Runnable() {                 @Override                 public void run() {                     // 得到時(shí)間對(duì)象                     Date date = new Date(finalI * 1000);                     // 執(zhí)行時(shí)間格式化                     formatAndPrint(date);                 }             });             // 啟動(dòng)線程             thread.start();         }     }     /**      * 格式化并打印時(shí)間      * @param date 時(shí)間對(duì)象      */     private static void formatAndPrint(Date date) {         // 格式化時(shí)間對(duì)象         SimpleDateFormat simpleDateFormat = new SimpleDateFormat("mm:ss");         // 執(zhí)行格式化         String result = simpleDateFormat.format(date);         // 打印最終結(jié)果         System.out.println("時(shí)間:" + result);     } }

以上程序的執(zhí)行結(jié)果為:

ThreadLocal好不好用

從上述結(jié)果可以看出,雖然此時(shí)創(chuàng)建的線程數(shù)和 SimpleDateFormat 的數(shù)量不算少,但程序還是可以正常運(yùn)行的。

③ 1000個(gè)線程格式化

然而當(dāng)我們將線程的數(shù)量從 10 個(gè)變成 1000 個(gè)的時(shí)候,我們就不能單純的使用 for 循環(huán)來(lái)創(chuàng)建 1000  個(gè)線程的方式來(lái)解決問(wèn)題了,因?yàn)檫@樣頻繁的新建和銷(xiāo)毀線程會(huì)造成大量的系統(tǒng)開(kāi)銷(xiāo)和線程過(guò)度爭(zhēng)搶 CPU 資源的問(wèn)題。

所以經(jīng)過(guò)一番思考后,我們決定使用線程池來(lái)執(zhí)行這 1000  次的任務(wù),因?yàn)榫€程池可以復(fù)用線程資源,無(wú)需頻繁的新建和銷(xiāo)毀線程,也可以通過(guò)控制線程池中線程的數(shù)量來(lái)避免過(guò)多線程所導(dǎo)致的 CPU  資源過(guò)度爭(zhēng)搶和線程頻繁切換所造成的性能問(wèn)題,而且我們可以將 SimpleDateFormat 提升為全局變量,從而避免每次執(zhí)行都要新建  SimpleDateFormat 的問(wèn)題,于是我們寫(xiě)下了這樣的代碼:

import java.text.SimpleDateFormat; import java.util.Date; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit;  public class App {     // 時(shí)間格式化對(duì)象     private static SimpleDateFormat simpleDateFormat = new SimpleDateFormat("mm:ss");      public static void main(String[] args) throws InterruptedException {         // 創(chuàng)建線程池執(zhí)行任務(wù)         ThreadPoolExecutor threadPool = new ThreadPoolExecutor(10, 10, 60,                 TimeUnit.SECONDS, new LinkedBlockingQueue<>(1000));         for (int i = 0; i < 1000; i++) {             int finalI = i;             // 執(zhí)行任務(wù)             threadPool.execute(new Runnable() {                 @Override                 public void run() {                     // 得到時(shí)間對(duì)象                     Date date = new Date(finalI * 1000);                     // 執(zhí)行時(shí)間格式化                     formatAndPrint(date);                 }             });         }         // 線程池執(zhí)行完任務(wù)之后關(guān)閉         threadPool.shutdown();     }      /**      * 格式化并打印時(shí)間      * @param date 時(shí)間對(duì)象      */     private static void formatAndPrint(Date date) {         // 執(zhí)行格式化         String result = simpleDateFormat.format(date);         // 打印最終結(jié)果         System.out.println("時(shí)間:" + result);     } }

以上程序的執(zhí)行結(jié)果為:

ThreadLocal好不好用

當(dāng)我們懷著無(wú)比喜悅的心情去運(yùn)行程序的時(shí)候,卻發(fā)現(xiàn)意外發(fā)生了,這樣寫(xiě)代碼竟然會(huì)出現(xiàn)線程安全的問(wèn)題。從上述結(jié)果可以看出,程序的打印結(jié)果竟然有重復(fù)內(nèi)容的,正確的情況應(yīng)該是沒(méi)有重復(fù)的時(shí)間才對(duì)。

PS:所謂的線程安全問(wèn)題是指:在多線程的執(zhí)行中,程序的執(zhí)行結(jié)果與預(yù)期結(jié)果不相符的情況。

a) 線程安全問(wèn)題分析

為了找到問(wèn)題所在,我們嘗試查看 SimpleDateFormat 中 format 方法的源碼來(lái)排查一下問(wèn)題,format 源碼如下:

private StringBuffer format(Date date, StringBuffer toAppendTo,                                 FieldDelegate delegate) {     // 注意此行代碼     calendar.setTime(date);      boolean useDateFormatSymbols = useDateFormatSymbols();      for (int i = 0; i < compiledPattern.length; ) {         int tag = compiledPattern[i] >>> 8;         int count = compiledPattern[i++] & 0xff;         if (count == 255) {             count = compiledPattern[i++] << 16;             count |= compiledPattern[i++];         }          switch (tag) {             case TAG_QUOTE_ASCII_CHAR:                 toAppendTo.append((char)count);                 break;              case TAG_QUOTE_CHARS:                 toAppendTo.append(compiledPattern, i, count);                 i += count;                 break;              default:                 subFormat(tag, count, delegate, toAppendTo, useDateFormatSymbols);                 break;         }     }     return toAppendTo; }

從上述源碼可以看出,在執(zhí)行 SimpleDateFormat.format 方法時(shí),會(huì)使用 calendar.setTime  方法將輸入的時(shí)間進(jìn)行轉(zhuǎn)換,那么我們想想一下這樣的場(chǎng)景:

  1. 鴻蒙官方戰(zhàn)略合作共建——HarmonyOS技術(shù)社區(qū)

  2. 線程 1 執(zhí)行了 calendar.setTime(date) 方法,將用戶輸入的時(shí)間轉(zhuǎn)換成了后面格式化時(shí)所需要的時(shí)間;

  3. 線程 1 暫停執(zhí)行,線程 2 得到 CPU 時(shí)間片開(kāi)始執(zhí)行;

  4. 線程 2 執(zhí)行了 calendar.setTime(date) 方法,對(duì)時(shí)間進(jìn)行了修改;

  5. 線程 2 暫停執(zhí)行,線程 1 得出 CPU 時(shí)間片繼續(xù)執(zhí)行,因?yàn)榫€程 1 和線程 2 使用的是同一對(duì)象,而時(shí)間已經(jīng)被線程 2 修改了,所以此時(shí)當(dāng)線程 1  繼續(xù)執(zhí)行的時(shí)候就會(huì)出現(xiàn)線程安全的問(wèn)題了。

正常的情況下,程序的執(zhí)行是這樣的:

ThreadLocal好不好用

非線程安全的執(zhí)行流程是這樣的:

ThreadLocal好不好用

b) 解決線程安全問(wèn)題:加鎖

當(dāng)出現(xiàn)線程安全問(wèn)題時(shí),我們想到的第一解決方案就是加鎖,具體的實(shí)現(xiàn)代碼如下:

import java.text.SimpleDateFormat; import java.util.Date; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit;  public class App {     // 時(shí)間格式化對(duì)象     private static SimpleDateFormat simpleDateFormat = new SimpleDateFormat("mm:ss");      public static void main(String[] args) throws InterruptedException {         // 創(chuàng)建線程池執(zhí)行任務(wù)         ThreadPoolExecutor threadPool = new ThreadPoolExecutor(10, 10, 60,                 TimeUnit.SECONDS, new LinkedBlockingQueue<>(1000));         for (int i = 0; i < 1000; i++) {             int finalI = i;             // 執(zhí)行任務(wù)             threadPool.execute(new Runnable() {                 @Override                 public void run() {                     // 得到時(shí)間對(duì)象                     Date date = new Date(finalI * 1000);                     // 執(zhí)行時(shí)間格式化                     formatAndPrint(date);                 }             });         }         // 線程池執(zhí)行完任務(wù)之后關(guān)閉         threadPool.shutdown();     }      /**      * 格式化并打印時(shí)間      * @param date 時(shí)間對(duì)象      */     private static void formatAndPrint(Date date) {         // 執(zhí)行格式化         String result = null;         // 加鎖         synchronized (App.class) {             result = simpleDateFormat.format(date);         }         // 打印最終結(jié)果         System.out.println("時(shí)間:" + result);     } }

以上程序的執(zhí)行結(jié)果為:

ThreadLocal好不好用

從上述結(jié)果可以看出,使用了 synchronized 加鎖之后程序就可以正常的執(zhí)行了。

加鎖的缺點(diǎn)

加鎖的方式雖然可以解決線程安全的問(wèn)題,但同時(shí)也帶來(lái)了新的問(wèn)題,當(dāng)程序加鎖之后,所有的線程必須排隊(duì)執(zhí)行某些業(yè)務(wù)才行,這樣無(wú)形中就降低了程序的運(yùn)行效率了。

有沒(méi)有既能解決線程安全問(wèn)題,又能提高程序的執(zhí)行速度的解決方案呢?

答案是:有的,這個(gè)時(shí)候 ThreadLocal就要上場(chǎng)了。

c) 解決線程安全問(wèn)題:ThreadLocal

1.ThreadLocal 介紹

ThreadLocal 從字面的意思來(lái)理解是線程本地變量的意思,也就是說(shuō)它是線程中的私有變量,每個(gè)線程只能使用自己的變量。

以上面線程池格式化時(shí)間為例,當(dāng)線程池中有 10 個(gè)線程時(shí),SimpleDateFormat 會(huì)存入 ThreadLocal 中,它也只會(huì)創(chuàng)建 10  個(gè)對(duì)象,即使要執(zhí)行 1000 次時(shí)間格式化任務(wù),依然只會(huì)新建 10 個(gè) SimpleDateFormat 對(duì)象,每個(gè)線程調(diào)用自己的 ThreadLocal  變量。

2.ThreadLocal 基礎(chǔ)使用

ThreadLocal 常用的核心方法有三個(gè):

  1. 鴻蒙官方戰(zhàn)略合作共建——HarmonyOS技術(shù)社區(qū)

  2. set 方法:用于設(shè)置線程獨(dú)立變量副本。沒(méi)有 set 操作的 ThreadLocal 容易引起臟數(shù)據(jù)。

  3. get 方法:用于獲取線程獨(dú)立變量副本。沒(méi)有 get 操作的 ThreadLocal 對(duì)象沒(méi)有意義。

  4. remove 方法:用于移除線程獨(dú)立變量副本。沒(méi)有 remove 操作容易引起內(nèi)存泄漏。

ThreadLocal 所有方法如下圖所示:

ThreadLocal好不好用

官方說(shuō)明文檔:https://docs.oracle.com/javase/8/docs/api/

ThreadLocal 基礎(chǔ)用法如下:

/**  * @公眾號(hào):Java中文社群  */ public class ThreadLocalExample {     // 創(chuàng)建一個(gè) ThreadLocal 對(duì)象     private static ThreadLocal<String> threadLocal = new ThreadLocal<>();      public static void main(String[] args) {         // 線程執(zhí)行任務(wù)         Runnable runnable = new Runnable() {             @Override             public void run() {                 String threadName = Thread.currentThread().getName();                 System.out.println(threadName + " 存入值:" + threadName);                 // 在 ThreadLocal 中設(shè)置值                 threadLocal.set(threadName);                 // 執(zhí)行方法,打印線程中設(shè)置的值                 print(threadName);             }         };         // 創(chuàng)建并啟動(dòng)線程 1         new Thread(runnable, "MyThread-1").start();         // 創(chuàng)建并啟動(dòng)線程 2         new Thread(runnable, "MyThread-2").start();     }      /**      * 打印線程中的 ThreadLocal 值      * @param threadName 線程名稱(chēng)      */     private static void print(String threadName) {         try {             // 得到 ThreadLocal 中的值             String result = threadLocal.get();             // 打印結(jié)果             System.out.println(threadName + " 取出值:" + result);         } finally {             // 移除 ThreadLocal 中的值(防止內(nèi)存溢出)             threadLocal.remove();         }     } }

以上程序的執(zhí)行結(jié)果為:

ThreadLocal好不好用

從上述結(jié)果可以看出,每個(gè)線程只會(huì)讀取到屬于自己的 ThreadLocal 值。

3.ThreadLocal 高級(jí)用法

① 初始化:initialValue

public class ThreadLocalByInitExample {     // 定義 ThreadLocal     private static ThreadLocal<String> threadLocal = new ThreadLocal(){         @Override         protected String initialValue() {             System.out.println("執(zhí)行 initialValue() 方法");             return "默認(rèn)值";         }     };      public static void main(String[] args) {         // 線程執(zhí)行任務(wù)         Runnable runnable = new Runnable() {             @Override             public void run() {                 // 執(zhí)行方法,打印線程中數(shù)據(jù)(未設(shè)置值打印)                 print(threadName);             }         };         // 創(chuàng)建并啟動(dòng)線程 1         new Thread(runnable, "MyThread-1").start();         // 創(chuàng)建并啟動(dòng)線程 2         new Thread(runnable, "MyThread-2").start();     }      /**      * 打印線程中的 ThreadLocal 值      * @param threadName 線程名稱(chēng)      */     private static void print(String threadName) {         // 得到 ThreadLocal 中的值         String result = threadLocal.get();         // 打印結(jié)果         System.out.println(threadName + " 得到值:" + result);     } }

以上程序的執(zhí)行結(jié)果為:

ThreadLocal好不好用

當(dāng)使用了 #threadLocal.set 方法之后,initialValue 方法就不會(huì)被執(zhí)行了,如下代碼所示:

public class ThreadLocalByInitExample {     // 定義 ThreadLocal     private static ThreadLocal<String> threadLocal = new ThreadLocal() {         @Override         protected String initialValue() {             System.out.println("執(zhí)行 initialValue() 方法");             return "默認(rèn)值";         }     };      public static void main(String[] args) {         // 線程執(zhí)行任務(wù)         Runnable runnable = new Runnable() {             @Override             public void run() {                 String threadName = Thread.currentThread().getName();                 System.out.println(threadName + " 存入值:" + threadName);                 // 在 ThreadLocal 中設(shè)置值                 threadLocal.set(threadName);                 // 執(zhí)行方法,打印線程中設(shè)置的值                 print(threadName);             }         };         // 創(chuàng)建并啟動(dòng)線程 1         new Thread(runnable, "MyThread-1").start();         // 創(chuàng)建并啟動(dòng)線程 2         new Thread(runnable, "MyThread-2").start();     }      /**      * 打印線程中的 ThreadLocal 值      * @param threadName 線程名稱(chēng)      */     private static void print(String threadName) {         try {             // 得到 ThreadLocal 中的值             String result = threadLocal.get();             // 打印結(jié)果             System.out.println(threadName + "取出值:" + result);         } finally {             // 移除 ThreadLocal 中的值(防止內(nèi)存溢出)             threadLocal.remove();         }     } }

以上程序的執(zhí)行結(jié)果為:

ThreadLocal好不好用

為什么 set 之后,初始化代碼就不執(zhí)行了?

要理解這個(gè)問(wèn)題,需要從 ThreadLocal.get() 方法的源碼中得到答案,因?yàn)槌跏蓟椒?initialValue 在 ThreadLocal  創(chuàng)建時(shí)并不會(huì)立即執(zhí)行,而是在調(diào)用了 get 方法只會(huì)才會(huì)執(zhí)行,測(cè)試代碼如下:

import java.util.Date;  public class ThreadLocalByInitExample {     // 定義 ThreadLocal     private static ThreadLocal<String> threadLocal = new ThreadLocal() {         @Override         protected String initialValue() {             System.out.println("執(zhí)行 initialValue() 方法 " + new Date());             return "默認(rèn)值";         }     };     public static void main(String[] args) {         // 線程執(zhí)行任務(wù)         Runnable runnable = new Runnable() {             @Override             public void run() {                 // 得到當(dāng)前線程名稱(chēng)                 String threadName = Thread.currentThread().getName();                 // 執(zhí)行方法,打印線程中設(shè)置的值                 print(threadName);             }         };         // 創(chuàng)建并啟動(dòng)線程 1         new Thread(runnable, "MyThread-1").start();         // 創(chuàng)建并啟動(dòng)線程 2         new Thread(runnable, "MyThread-2").start();     }      /**      * 打印線程中的 ThreadLocal 值      * @param threadName 線程名稱(chēng)      */     private static void print(String threadName) {         System.out.println("進(jìn)入 print() 方法 " + new Date());         try {             // 休眠 1s             Thread.sleep(1000);         } catch (InterruptedException e) {             e.printStackTrace();         }         // 得到 ThreadLocal 中的值         String result = threadLocal.get();         // 打印結(jié)果         System.out.println(String.format("%s 取得值:%s %s",                 threadName, result, new Date()));     } }

以上程序的執(zhí)行結(jié)果為:

ThreadLocal好不好用

從上述打印的時(shí)間可以看出:initialValue 方法并不是在 ThreadLocal 創(chuàng)建時(shí)執(zhí)行的,而是在調(diào)用 Thread.get  方法時(shí)才執(zhí)行的。

接下來(lái)來(lái)看 Threadlocal.get 源碼的實(shí)現(xiàn):

public T get() {     // 得到當(dāng)前的線程     Thread t = Thread.currentThread();     ThreadLocalMap map = getMap(t);     // 判斷 ThreadLocal 中是否有數(shù)據(jù)     if (map != null) {         ThreadLocalMap.Entry e = map.getEntry(this);         if (e != null) {             @SuppressWarnings("unchecked")             T result = (T)e.value;             // 有 set 值,直接返回?cái)?shù)據(jù)             return result;         }     }     // 執(zhí)行初始化方法【重點(diǎn)關(guān)注】     return setInitialValue(); } private T setInitialValue() {     // 執(zhí)行初始化方法【重點(diǎn)關(guān)注】     T value = initialValue();     Thread t = Thread.currentThread();     ThreadLocalMap map = getMap(t);     if (map != null)         map.set(this, value);     else         createMap(t, value);     return value; }

從上述源碼可以看出,當(dāng) ThreadLocal 中有值時(shí)會(huì)直接返回值 e.value,只有 Threadlocal 中沒(méi)有任何值時(shí)才會(huì)執(zhí)行初始化方法  initialValue。

注意事項(xiàng)&mdash;類(lèi)型必須保持一致

注意在使用 initialValue 時(shí),返回值的類(lèi)型要和 ThreadLoca 定義的數(shù)據(jù)類(lèi)型保持一致,如下圖所示:

ThreadLocal好不好用

如果數(shù)據(jù)不一致就會(huì)造成 ClassCaseException 類(lèi)型轉(zhuǎn)換異常,如下圖所示:

ThreadLocal好不好用

② 初始化2:withInitial

import java.util.function.Supplier;  public class ThreadLocalByInitExample {     // 定義 ThreadLocal     private static ThreadLocal<String> threadLocal =             ThreadLocal.withInitial(new Supplier<String>() {                 @Override                 public String get() {                     System.out.println("執(zhí)行 withInitial() 方法");                     return "默認(rèn)值";                 }             });     public static void main(String[] args) {         // 線程執(zhí)行任務(wù)         Runnable runnable = new Runnable() {             @Override             public void run() {                 String threadName = Thread.currentThread().getName();                 // 執(zhí)行方法,打印線程中設(shè)置的值                 print(threadName);             }         };         // 創(chuàng)建并啟動(dòng)線程 1         new Thread(runnable, "MyThread-1").start();         // 創(chuàng)建并啟動(dòng)線程 2         new Thread(runnable, "MyThread-2").start();     }      /**      * 打印線程中的 ThreadLocal 值      * @param threadName 線程名稱(chēng)      */     private static void print(String threadName) {         // 得到 ThreadLocal 中的值         String result = threadLocal.get();         // 打印結(jié)果         System.out.println(threadName + " 得到值:" + result);     } }

以上程序的執(zhí)行結(jié)果為:

ThreadLocal好不好用

通過(guò)上述的代碼發(fā)現(xiàn),withInitial 方法的使用好和 initialValue  好像沒(méi)啥區(qū)別,那為啥還要造出兩個(gè)類(lèi)似的方法呢?客官莫著急,繼續(xù)往下看。

③ 更簡(jiǎn)潔的 withInitial 使用

withInitial 方法的優(yōu)勢(shì)在于可以更簡(jiǎn)單的實(shí)現(xiàn)變量初始化,如下代碼所示:

public class ThreadLocalByInitExample {     // 定義 ThreadLocal     private static ThreadLocal<String> threadLocal = ThreadLocal.withInitial(() -> "默認(rèn)值");     public static void main(String[] args) {         // 線程執(zhí)行任務(wù)         Runnable runnable = new Runnable() {             @Override             public void run() {                 String threadName = Thread.currentThread().getName();                 // 執(zhí)行方法,打印線程中設(shè)置的值                 print(threadName);             }         };         // 創(chuàng)建并啟動(dòng)線程 1         new Thread(runnable, "MyThread-1").start();         // 創(chuàng)建并啟動(dòng)線程 2         new Thread(runnable, "MyThread-2").start();     }      /**      * 打印線程中的 ThreadLocal 值      * @param threadName 線程名稱(chēng)      */     private static void print(String threadName) {         // 得到 ThreadLocal 中的值         String result = threadLocal.get();         // 打印結(jié)果         System.out.println(threadName + " 得到值:" + result);     } }

以上程序的執(zhí)行結(jié)果為:

ThreadLocal好不好用

4.ThreadLocal 版時(shí)間格式化

了解了 ThreadLocal 的使用之后,我們回到本文的主題,接下來(lái)我們將使用 ThreadLocal 來(lái)實(shí)現(xiàn) 1000  個(gè)時(shí)間的格式化,具體實(shí)現(xiàn)代碼如下:

import java.text.SimpleDateFormat; import java.util.Date; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit;  public class MyThreadLocalByDateFormat {     // 創(chuàng)建 ThreadLocal 并設(shè)置默認(rèn)值     private static ThreadLocal<SimpleDateFormat> dateFormatThreadLocal =             ThreadLocal.withInitial(() -> new SimpleDateFormat("mm:ss"));      public static void main(String[] args) {         // 創(chuàng)建線程池執(zhí)行任務(wù)         ThreadPoolExecutor threadPool = new ThreadPoolExecutor(10, 10, 60,                 TimeUnit.SECONDS, new LinkedBlockingQueue<>(1000));         // 執(zhí)行任務(wù)         for (int i = 0; i < 1000; i++) {             int finalI = i;             // 執(zhí)行任務(wù)             threadPool.execute(new Runnable() {                 @Override                 public void run() {                     // 得到時(shí)間對(duì)象                     Date date = new Date(finalI * 1000);                     // 執(zhí)行時(shí)間格式化                     formatAndPrint(date);                 }             });         }         // 線程池執(zhí)行完任務(wù)之后關(guān)閉         threadPool.shutdown();         // 線程池執(zhí)行完任務(wù)之后關(guān)閉         threadPool.shutdown();     }     /**      * 格式化并打印時(shí)間      * @param date 時(shí)間對(duì)象      */     private static void formatAndPrint(Date date) {         // 執(zhí)行格式化         String result = dateFormatThreadLocal.get().format(date);         // 打印最終結(jié)果         System.out.println("時(shí)間:" + result);     } }

以上程序的執(zhí)行結(jié)果為:

ThreadLocal好不好用

從上述結(jié)果可以看出,使用 ThreadLocal 也可以解決線程并發(fā)問(wèn)題,并且避免了代碼加鎖排隊(duì)執(zhí)行的問(wèn)題。

使用場(chǎng)景2:跨類(lèi)傳遞數(shù)據(jù)

除了上面的使用場(chǎng)景之外,我們還可以使用 ThreadLocal 來(lái)實(shí)現(xiàn)線程中跨類(lèi)、跨方法的數(shù)據(jù)傳遞。比如登錄用戶的 User  對(duì)象信息,我們需要在不同的子系統(tǒng)中多次使用,如果使用傳統(tǒng)的方式,我們需要使用方法傳參和返回值的方式來(lái)傳遞 User  對(duì)象,然而這樣就無(wú)形中造成了類(lèi)和類(lèi)之間,甚至是系統(tǒng)和系統(tǒng)之間的相互耦合了,所以此時(shí)我們可以使用 ThreadLocal 來(lái)實(shí)現(xiàn) User 對(duì)象的傳遞。

確定了方案之后,接下來(lái)我們來(lái)實(shí)現(xiàn)具體的業(yè)務(wù)代碼。我們可以先在主線程中構(gòu)造并初始化一個(gè) User 對(duì)象,并將此 User 對(duì)象存儲(chǔ)在 ThreadLocal  中,存儲(chǔ)完成之后,我們就可以在同一個(gè)線程的其他類(lèi)中,如倉(cāng)儲(chǔ)類(lèi)或訂單類(lèi)中直接獲取并使用 User 對(duì)象了,具體實(shí)現(xiàn)代碼如下。

主線程中的業(yè)務(wù)代碼:

public class ThreadLocalByUser {     public static void main(String[] args) {         // 初始化用戶信息         User user = new User("Java");         // 將 User 對(duì)象存儲(chǔ)在 ThreadLocal 中         UserStorage.setUser(user);         // 調(diào)用訂單系統(tǒng)         OrderSystem orderSystem = new OrderSystem();         // 添加訂單(方法內(nèi)獲取用戶信息)         orderSystem.add();         // 調(diào)用倉(cāng)儲(chǔ)系統(tǒng)         RepertorySystem repertory = new RepertorySystem();         // 減庫(kù)存(方法內(nèi)獲取用戶信息)         repertory.decrement();     } }

User 實(shí)體類(lèi):

/**  * 用戶實(shí)體類(lèi)  */ class User {     public User(String name) {         this.name = name;     }     private String name;     public String getName() {         return name;     }     public void setName(String name) {         this.name = name;     } }

ThreadLocal 操作類(lèi):

/**  * 用戶信息存儲(chǔ)類(lèi)  */ class UserStorage {     // 用戶信息     public static ThreadLocal<User> USER = new ThreadLocal();      /**      * 存儲(chǔ)用戶信息      * @param user 用戶數(shù)據(jù)      */     public static void setUser(User user) {         USER.set(user);     } }

* 訂單類(lèi)

/**  * 訂單類(lèi)  */ class OrderSystem {     /**      * 訂單添加方法      */     public void add() {         // 得到用戶信息         User user = UserStorage.USER.get();         // 業(yè)務(wù)處理代碼(忽略)...         System.out.println(String.format("訂單系統(tǒng)收到用戶:%s 的請(qǐng)求。",                 user.getName()));     } }

倉(cāng)儲(chǔ)類(lèi):

/**  * 倉(cāng)儲(chǔ)類(lèi)  */ class RepertorySystem {     /**      * 減庫(kù)存方法      */     public void decrement() {         // 得到用戶信息         User user = UserStorage.USER.get();         // 業(yè)務(wù)處理代碼(忽略)...         System.out.println(String.format("倉(cāng)儲(chǔ)系統(tǒng)收到用戶:%s 的請(qǐng)求。",                 user.getName()));     } }

以上程序的最終執(zhí)行結(jié)果:

ThreadLocal好不好用

從上述結(jié)果可以看出,當(dāng)我們?cè)谥骶€程中先初始化了 User 對(duì)象之后,訂單類(lèi)和倉(cāng)儲(chǔ)類(lèi)無(wú)需進(jìn)行任何的參數(shù)傳遞也可以正常獲得 User  對(duì)象了,從而實(shí)現(xiàn)了一個(gè)線程中,跨類(lèi)和跨方法的數(shù)據(jù)傳遞。

總結(jié)

使用 ThreadLocal 可以創(chuàng)建線程私有變量,所以不會(huì)導(dǎo)致線程安全問(wèn)題,同時(shí)使用 ThreadLocal  還可以避免因?yàn)橐腈i而造成線程排隊(duì)執(zhí)行所帶來(lái)的性能消耗;再者使用 ThreadLocal 還可以實(shí)現(xiàn)一個(gè)線程內(nèi)跨類(lèi)、跨方法的數(shù)據(jù)傳遞。

到此,關(guān)于“ThreadLocal好不好用”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識(shí),請(qǐng)繼續(xù)關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編會(huì)繼續(xù)努力為大家?guī)?lái)更多實(shí)用的文章!

本文標(biāo)題:ThreadLocal好不好用
本文路徑:http://www.chinadenli.net/article2/pigoic.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供手機(jī)網(wǎng)站建設(shè)移動(dòng)網(wǎng)站建設(shè)網(wǎng)站制作軟件開(kāi)發(fā)域名注冊(cè)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

成都seo排名網(wǎng)站優(yōu)化