国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

ThreadLocal原理及其實際應用

2019-11-14 21:59:48
字體:
來源:轉載
供稿:網友
ThreadLocal原理及其實際應用前言

java猿在面試中,經常會被問到1個問題:java實現同步有哪幾種方式?

大家一般都會回答使用synchronized, 那么還有其他方式嗎? 答案是肯定的, 另外一種方式也就是本文要說的ThreadLocal。

ThreadLocal介紹

ThreadLocal, 看名字也能猜到, "線程本地", "線程本地變量"。 我們看下官方的一段話:

This class PRovides thread-local variables. These variables differ from their normal counterparts in that each thread that accesses one (via its get or set method) has its own, independently initialized copy of the variable. ThreadLocal instances are typically private static fields in classes that wish to associate state with a thread (e.g., a user ID or Transaction ID). 

粗略地翻譯一下:ThreadLocal這個類提供線程本地的變量。這些變量與一般正常的變量不同,它們在每個線程中都是獨立的。ThreadLocal實例最典型的運用就是在類的私有靜態變量中定義,并與線程關聯。

什么意思呢? 下面我們通過1個實例來說明一下:

jdk中的SimpleDateFormat類不是一個線程安全的類,在多線程中使用會出現問題,我們會通過線程同步來處理:

  1. 使用synchronized

    private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");public synchronized static String formatDate(Date date) {    return sdf.format(date);}
  2. 使用ThreadLocal

    private static final ThreadLocal<SimpleDateFormat> formatter = new ThreadLocal<SimpleDateFormat>(){    @Override    protected SimpleDateFormat initialValue()    {        return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");    }};public String formatIt(Date date){    return formatter.get().format(date);}

這兩種方式是一樣的,只不過一種用了synchronized,另外一種用了ThreadLocal。

synchronized和ThreadLocal的區別

使用synchronized的話,表示當前只有1個線程才能訪問方法,其他線程都會被阻塞。當訪問的線程也阻塞的時候,其他所有訪問該方法的線程全部都會阻塞,這個方法相當地 "耗時"。使用ThreadLocal的話,表示每個線程的本地變量中都有SimpleDateFormat這個實例的引用,也就是各個線程之間完全沒有關系,也就不存在同步問題了。

綜合來說:使用synchronized是一種 "以時間換空間"的概念, 而使用ThreadLocal則是 "以空間換時間"的概念。

ThreadLocal原理分析

我們先看下ThreadLocal的類結構:

我們看到ThreadLocal內部有個ThreadLocalMap內部類,ThreadLocalMap內部有個Entry內部類。

先介紹一下ThreadLocalMap和ThreadLocalMap.Entry內部類:ThreadLocalMap其實也就是一個為ThreadLocal服務的自定義的hashmap類。Entry是一個繼承WeakReference類的類,也就是ThreadLocalMap這個hash map中的每一項,并且Entry中的key基本上都是ThreadLocal。

再下來我們看下Thread線程類:Thread線程類內部有個ThreadLocal.ThreadLocalMap類型的屬性:

/* ThreadLocal values pertaining to this thread. This map is maintained * by the ThreadLocal class. */ThreadLocal.ThreadLocalMap threadLocals = null;

下面重點來看ThreadLocal類的源碼

public T get() {// 得到當前線程    Thread t = Thread.currentThread();    // 拿到當前線程的ThreadLocalMap對象    ThreadLocalMap map = getMap(t);    if (map != null) {    // 找到該ThreadLocal對應的Entry        ThreadLocalMap.Entry e = map.getEntry(this);        if (e != null) {            @SuppressWarnings("unchecked")            T result = (T)e.value;            return result;        }    }    // 當前線程沒有ThreadLocalMap對象的話,那么就初始化ThreadLocalMap    return setInitialValue();}private T setInitialValue() {// 初始化ThreadLocalMap,默認返回null,可由子類擴展    T value = initialValue();    Thread t = Thread.currentThread();    ThreadLocalMap map = getMap(t);    if (map != null)    // 實例化ThreadLocalMap之后,將初始值丟入到Map中        map.set(this, value);    else        createMap(t, value);    return value;}void createMap(Thread t, T firstValue) {    t.threadLocals = new ThreadLocalMap(this, firstValue);}ThreadLocalMap getMap(Thread t) {    return t.threadLocals;}public void set(T value) {// set邏輯:找到當前線程的ThreadLocalMap,找到的話,設置對應的值,否則創建ThreadLocalMap    Thread t = Thread.currentThread();    ThreadLocalMap map = getMap(t);    if (map != null)        map.set(this, value);    else        createMap(t, value);}

注釋已經寫了,讀者有不明白的可以自己看看源碼。

ThreadLocal的應用

ThreadLocal應用廣泛,下面介紹下在SpringMVC中的應用。

RequestContextHolder內部結構

RequestContextHolder:該類會暴露與線程綁定的RequestAttributes對象,什么意思呢? 就是說web請求過來的數據可以跟線程綁定, 用戶A,用戶B分別請求過來,可以使用RequestContextHolder得到各個請求的數據。

RequestContextHolder數據結構:

具體這兩個holder:

private static final ThreadLocal<RequestAttributes> requestAttributesHolder =new NamedThreadLocal<RequestAttributes>("Request attributes");private static final ThreadLocal<RequestAttributes> inheritableRequestAttributesHolder =new NamedInheritableThreadLocal<RequestAttributes>("Request context");        

這里的NamedThreadLocal只是1個帶name屬性的ThreadLocal:

public class NamedThreadLocal<T> extends ThreadLocal<T> {    private final String name;    public NamedThreadLocal(String name) {        Assert.hasText(name, "Name must not be empty");        this.name = name;    }    @Override    public String toString() {        return this.name;    }}

繼續看下RequestContextHolder的getRequestAttributes方法,其中接口RequestAttributes是對請求request的封裝:

public static RequestAttributes getRequestAttributes() {// 直接從ThreadLocalContext拿當前線程的RequestAttributesRequestAttributes attributes = requestAttributesHolder.get();if (attributes == null) {attributes = inheritableRequestAttributesHolder.get();}return attributes;}

我們看到,這里直接使用了ThreadLocal的get方法得到了RequestAttributes。當需要得到Request的時候執行:

ServletRequestAttributes requestAttributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();HttpServletRequest request = requestAttributes.getRequest();
RequestContextHolder的初始化

以下代碼在FrameworkServlet代碼中:

總結

本文介紹了ThreadLocal的原理以及ThreadLocal在SpringMVC中的應用。個人感覺ThreadLocal應用場景更多是共享一個變量,但是該變量又不是線程安全的,而不是線程同步。比如RequestContextHolder、LocaleContextHolder、SimpleDateFormat等的共享應用。


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 左贡县| 比如县| 太原市| 临武县| 原平市| 新和县| 翁牛特旗| 武隆县| 砚山县| 景德镇市| 永丰县| 青铜峡市| 福州市| 桓仁| 高安市| 从江县| 深泽县| 交城县| 社旗县| 郁南县| 高阳县| 怀来县| 库尔勒市| 阿合奇县| 赫章县| 大方县| 抚松县| 阳泉市| 黄龙县| 苍山县| 阿瓦提县| 保靖县| 安新县| 泌阳县| 冕宁县| 房山区| 陕西省| 安顺市| 黄冈市| 奈曼旗| 资中县|