ThreadLocal
根據我的理解,ThreadLocal就是爲每個Thread(線程)創建一個對象的copy,根據ThreadLocal的引用在map中獲得相應的對象值,這樣在多個線程同時操作同一個ThreadLocal對象的時候,其並不是操作的原始的對象,而是複製的對象,所以多個線程就不會相互影響了。 注意,主線程和new出來的線程其實是不同的線程,所以主線程對於ThreadLocal的賦值其實是無效的,甚至於靜態代碼塊的操作,都是對新的線程不可見的,所以一般來說,new ThreadLocal的時候可以重寫他的initValue方法,這樣的操作是對新線程可見的
在子線程中獲取ThreadLocal,然後獲得其value,按理來說是不一樣的,但是如果我們把他放到線程池裏面執行呢?一切就有點意思了
public class Local {
private static final ThreadLocal<Person> local = new ThreadLocal<Person>(){
@Override
protected Person initialValue() {
return new Person();
}
};
public static void main(String[] args) throws InterruptedException {
ExecutorService pool = Executors.newFixedThreadPool(98)
for (int i = 0; i < 100;i++) {
int finalI = i;
Runnable r = () ->{
System.out.println(local.get().getName());
local.get().setName("wang"+ finalI);
};
pool.execute(r);
Thread.sleep(10l);
}
}
}
定長線程池
定長線程池,可控制線程最大併發數,超出的線程會在隊列中等待。
我們可以看到結果是
一片null 只有最後兩行是wang0 和wang1
爲什麼呢?因爲最後2個任務在池隊列等待,然後進去了之後,分配了前兩個線程。而前兩個線程已經處理過一次ThreadLocal了,再次獲取的時候就直接可以獲取之前的值,所以打印出來的就是wang0 ,wang 1
稍微有些常識的人都能看出來,如果繼續在第一個和第二個線程裏寫入 Thread.sleep(1000l); 那麼打印出來的就是wang2 wang3
如果我們將池的大小設置爲96 ,即將執行的Runnable數爲100,去除線程睡眠,那麼 就會有4個線程可以獲得之前已經處理過的ThreadLocal (因爲執行時間很短而且執行任務數目不多,出現一個線程被2次以上覆用的情況幾乎不存在)。打印出來的就會有隨機4個wang +數字
玩完了,所以這兩者結合的時候,雖然不會有線程安全的問題。但是可能會有很明顯的數據複用問題。注意注意