ThreadLocal與InheritableThreadLocal區別

最近項目中爲了讓打印的日誌能夠追蹤,看到了Logback和log4j2支持的MDC功能,其內部利用了子線程從父線程繼承InheritableThreadLocal類型變量的特性。之前只使用到了ThreadLocal,在這裏作一下對比,並簡單的作了一些分析java

ThreadLocal與InheritableThreadLocal區別

ThreadLocal聲明的變量是線程私有的成員變量,每一個線程都有該變量的副本,線程對變量的修改對其餘線程不可見數組

InheritableThreadLocal聲明的變量一樣是線程私有的,可是子線程可使用一樣的InheritableThreadLocal類型變量從父線程繼承InheritableThreadLocal聲明的變量,父線程沒法拿到其子線程的。即便能夠繼承,可是子線程對變量的修改對父線程也是不可見的。ide

原理

ThreadLocal原理:

Thread有一個類型爲ThreadLocal.ThreadLocalMap變量:threadLocals,ThreadLocalMap底層是一個Entry數組,用於保存ThreadLocal引用和真正變量(程序中銷售人的主管姓名)的值測試

看下面的代碼和註釋瞭解如何將值放入threadLocals的this

調用ThreadLocal對象的set()方法spa

   public void set(T value) {
        //獲取到當前線程對象
        Thread t = Thread.currentThread();
        //從當前線程對象中拿threadLocals變量
        ThreadLocalMap map = getMap(t);
        //若是threadLocals不爲空,則調用它的set方法,將key=當前ThreadLocal對象 value=主管姓名
        if (map != null)
            map.set(this, value);
        //若是threadLocals爲空,則新建立一個ThreadLocalMap
        else
            createMap(t, value);
    }

 

InheritableThreadLocal原理:

InheritableThreadLocal繼承自ThreadLocal,所以能夠想到它本身要麼重寫,要麼新增了部分方法。從源碼看只是重寫了childValue、getMap、createMap方法,其中getMap和createMap都是父類set方法中使用到的,所以主要是改變了父類set方法的行爲。線程

getMap獲取的是InheritableThreadLocal類型的變量日誌

createMap建立的是InheritableThreadLocal類型的實例對象

可是從這點上看是不能達到子線程繼承父線程的變量的。再看看Thread的init方法中比之前新增了以下代碼:blog

//若是當前線程的InheritableThreadLocal類型變量inheritThreadLocals爲空且父線程的
//inheritThreadLocals不爲空,則複製父線程的inheritThreadLocals中的內容給當前線程的
//inheritThreadLocals,是值複製不是引用賦值
if (inheritThreadLocals && parent.inheritableThreadLocals != null)
            this.inheritableThreadLocals =
                ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);

上面說到重寫了三個方法,其中的childValue尚未找到使用的地方,如今該它出場了,就是在繼承變量的時候,建立新的ThreadLocalMap使用到了。

createInheritedMap->ThreadLocalMap->{

//建立一個新的Entry數組

table = new Entry[len];

//複製數據

Object value = key.childValue(e.value);
Entry c = new Entry(key, value);
table[h] = c;

}

測試代碼:

ThreadLocal測試

寫一個銷售人類,該類有一個主管人員成員變量(ThreadLocal聲明),將銷售人對象傳給不一樣的工做線程,在工做線程中打印銷售人的主管,其結果應該是爲NULL,由於主管是聲明爲線程本地變量,是不能共享的

package com.jv.jdk.thread.threadlocal;
​
import lombok.Data;
​
@Data
public class Salesman {
    private ThreadLocal<String> charger = new ThreadLocal<>();
    public Salesman(String charger){
        this.charger.set(charger);
    }
}
package com.jv.jdk.thread.threadlocal;
​
​
import lombok.extern.log4j.Log4j2;   //@Log4j2註解能夠很方便作日誌輸出
​
import java.util.concurrent.CountDownLatch;
​
​
@Log4j2
public class Worker implements Runnable{
    private Salesman salesman = null;
    private CountDownLatch countDownLatch = null;
​
    public Worker(Salesman salesman, CountDownLatch countDownLatch){
        this.salesman = salesman;
        this.countDownLatch = countDownLatch;
    }
    @Override
    public void run() {
        log.info(Thread.currentThread().getName()+":"+salesman.getCharger().get());
        countDownLatch.countDown();
    }
}

測試類

   @Test
    public void threadLocal(){
        CountDownLatch countDownLatch = new CountDownLatch(2);
        Salesman salesman = new Salesman("劉牆東");
        Thread thread_1 = new Thread(new Worker(salesman,countDownLatch));
        Thread thread_2 = new Thread(new Worker(salesman,countDownLatch));
        thread_1.start();
        thread_2.start();
        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

測試結果:

2018.08.30 at 14:47:45 CST 14-Thread-1 INFO  com.jv.jdk.thread.threadlocal.Worker run() @20  - Thread-1:null
2018.08.30 at 14:47:45 CST 15-Thread-2 INFO  com.jv.jdk.thread.threadlocal.Worker run() @20  - Thread-2:null

InheritableThreadLocal測試

與上面的代碼重寫SalesMan類

package com.jv.jdk.thread.threadlocal;
​
import lombok.Data;
​
@Data
public class SalesmanInherit extends Salesman{
    private InheritableThreadLocal<String> charger = new InheritableThreadLocal<>();
    public SalesmanInherit(String charger){
        super(charger);
        this.charger.set(charger);
    }
}
   @Test
    public void inheritablThreadLocal(){
        CountDownLatch countDownLatch = new CountDownLatch(2);
        Salesman salesman = new Salesman("劉牆東");
        SalesmanInherit salesmanInherit = new SalesmanInherit("劉牆東");
        Thread thread_1 = new Thread(new Worker(salesmanInherit,countDownLatch));
        Thread thread_2 = new Thread(new Worker(salesmanInherit,countDownLatch));
        thread_1.start();
        thread_2.start();
        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

測試結果:

2018.08.30 at 14:51:28 CST 15-Thread-2 INFO  com.jv.jdk.thread.threadlocal.Worker run() @20  - Thread-2:劉牆東
2018.08.30 at 14:51:28 CST 14-Thread-1 INFO  com.jv.jdk.thread.threadlocal.Worker run() @20  - Thread-1:劉牆東
相關文章
相關標籤/搜索