ThreadLocal 與 Thread 同步機制的比較
- Thread同步機制采用了以時間換空間方式,通過物件鎖保證在同一個時間,對于同一個實體物件,只有一個執行緒訪問,
- ThreadLocal 采用以空間換時間方式,為每一個執行緒都提供一份變數,各執行緒間同時訪問互不影響,
定義ThreadLocal的同時為當前執行緒的區域變數副本賦初始值
? 方式1:ThreadLocal#withInitial(Supplier<? extends S> supplier) withInitial的引數是函式式介面Supplier<T>private static ThreadLocal<StringBuilder> threadLocal = ThreadLocal.withInitial(StringBuilder::new);
? 方式2:覆寫protected方法initialValue():
private static ThreadLocal<StringBuilder> threadLocal = new ThreadLocal<StringBuilder>() { @Override protected StringBuilder initialValue() { return new StringBuilder(); } };
ThreadLocal無法解決共享物件的更新問題
對于如下代碼, threadLocal里操作的StringBuilder與全域StringBuilder是同一個記憶體物件, 所以,在多執行緒往自己的ThreadLocal里的StringBuilder里append資料的時候,操作的都是全域的StringBuilder, 所以,這段代碼定義的ThreadLocal<StringBuilder>沒有任何意義, 因為StringBuilder是執行緒不安全的,所以,會出現執行緒不安全問題:某些并發場景下會出現有的執行緒沒有append進去, 如果想實作執行緒安全,那么,不是用ThreadLocal,而是用執行緒安全的StringBuffer,或者借助執行緒同步鎖,static StringBuilder sb = new StringBuilder("init"); private static ThreadLocal<StringBuilder> threadLocal = ThreadLocal.withInitial(() -> sb);
所以說,最好不要用ThreadLocal來操作共享物件, 盡量僅讓其持有當前執行緒里的物件,
完整示例代碼 of ThreadLocal無法解決共享物件的更新問題
package jstudy.threadlocal; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.RandomUtils; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; /** * {@link java.lang.ThreadLocal}無法解決共享物件的更新問題,本代碼實體將證明這一點, * 結果雖然都append了,但是,是無序的 * 所以,使用某個參考來操作共享物件時,依然需要進行執行緒同步 */ @Slf4j public class InitValueInThreadLocal { static StringBuilder sb = new StringBuilder("init"); private static ThreadLocal<StringBuilder> threadLocal = ThreadLocal.withInitial(StringBuilder::new); static AtomicInteger integer = new AtomicInteger(); public void print() { StringBuilder stringBuilder = threadLocal.get(); int j = integer.getAndIncrement(); log.info("初始:{} val={}", stringBuilder.toString(), j); try { Thread.sleep(RandomUtils.nextInt(0, 5)); } catch (InterruptedException e) { e.printStackTrace(); } stringBuilder.append("-" + j); threadLocal.remove(); log.info(stringBuilder.toString()); } public static void main(String[] args) throws InterruptedException { for (int i = 0; i < 5; i++) { InitValueInThreadLocal1 ins = new InitValueInThreadLocal1(); new Thread(() -> ins.print()).start(); } TimeUnit.SECONDS.sleep(2); log.info(sb.toString()); } }
由于StringBuilder執行緒不安全,如下是 意料之外的結果,main方法里啟動了5個執行緒,所以,本應該把0、1、2、3、4這5個數字append到sb物件里,卻發現0沒有append進去,

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/415906.html
標籤:其他
