91精产品自偷自偷综合官网版下载-91精产品自偷自偷综合下-91精品-91精品91久久久-91精品成人-91精品成人www

網站建設資訊

NEWS

網站建設資訊

TransmittableThreadLocal怎么解決池化復用線程的傳值問題

這篇文章主要介紹“TransmittableThreadLocal怎么解決池化復用線程的傳值問題”,在日常操作中,相信很多人在TransmittableThreadLocal怎么解決池化復用線程的傳值問題問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”TransmittableThreadLocal怎么解決池化復用線程的傳值問題”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!

創新互聯公司服務項目包括當陽網站建設、當陽網站制作、當陽網頁制作以及當陽網絡營銷策劃等。多年來,我們專注于互聯網行業,利用自身積累的技術優勢、行業經驗、深度合作伙伴關系等,向廣大中小型企業、政府機構等提供互聯網行業的解決方案,當陽網站推廣取得了明顯的社會效益與經濟效益。目前,我們服務的客戶以成都為中心已經輻射到當陽省份的部分城市,未來相信會繼續擴大服務區域并繼續獲得客戶的支持與信任!

TransmittableThreadLocal

一般情況下,ThreadLocal都可以滿足我們的需求,當我們出現需要 在使用線程池等會池化復用線程的執行組件情況下傳遞ThreadLocal ,

這個場景就是TransmittableThreadLocal解決的問題。

Github地址:https://github.com/alibaba/transmittable-thread-local

感興趣的可以去下載的玩一玩,接下來我們來介紹一下這個組件的神奇之處。

首先看個demo, 通過demo,我們先了解了解怎么用

demo
/**
 * ttl測試
 *
 * @author zhangyunhe
 * @date 2020-04-23 12:47
 */
public class Test {

    // 1. 初始化一個TransmittableThreadLocal,這個是繼承了InheritableThreadLocal的
    static TransmittableThreadLocal local = new TransmittableThreadLocal<>();

    // 初始化一個長度為1的線程池
    static ExecutorService poolExecutor = Executors.newFixedThreadPool(1);

    public static void main(String[] args) throws ExecutionException, InterruptedException {

        Test test = new Test();
        test.test();
    }
    private void test() throws ExecutionException, InterruptedException {

        // 設置初始值
        local.set("天王老子");
        //!!!! 注意:這個地方的Task是使用了TtlRunnable包裝的
        Future future = poolExecutor.submit(TtlRunnable.get(new Task("任務1")));
        future.get();

        Future future2 = poolExecutor.submit(TtlRunnable.get(new Task("任務2")));
        future2.get();
      
        System.out.println("父線程的值:"+local.get());
        poolExecutor.shutdown();
    }

    class Task implements Runnable{

        String str;
        Task(String str){
            this.str = str;
        }
        @Override
        public void run() {
            // 獲取值
            System.out.println(Thread.currentThread().getName()+":"+local.get());
            // 重新設置一波
            local.set(str);
        }
    }
}

輸出結果:

pool-1-thread-1:天王老子
pool-1-thread-1:天王老子
父線程的值:天王老子

原理分析

我們首先看一下TransmittableThreadLocal的源碼,

public class TransmittableThreadLocal extends InheritableThreadLocal implements TtlCopier {
  
  // 1. 此處的holder是他的主要設計點,后續在構建TtlRunnable
  private static InheritableThreadLocal, ?>> holder =
            new InheritableThreadLocal, ?>>() {
                @Override
                protected WeakHashMap, ?> initialValue() {
                    return new WeakHashMap, Object>();
                }

                @Override
                protected WeakHashMap, ?> childValue(WeakHashMap, ?> parentValue) {
                    return new WeakHashMap, Object>(parentValue);
                }
            };

    @SuppressWarnings("unchecked")
    private void addThisToHolder() {
        if (!holder.get().containsKey(this)) {
            holder.get().put((TransmittableThreadLocal) this, null); // WeakHashMap supports null value.
        }
    }
  
  @Override
    public final T get() {
        T value = super.get();
        if (disableIgnoreNullValueSemantics || null != value) addThisToHolder();
        return value;
    }
    /**
     * see {@link InheritableThreadLocal#set}
     */
    @Override
    public final void set(T value) {
        if (!disableIgnoreNullValueSemantics && null == value) {
            // may set null to remove value
            remove();
        } else {
            super.set(value);
            addThisToHolder();
        }
    }
    /**
     * see {@link InheritableThreadLocal#remove()}
     */
    @Override
    public final void remove() {
        removeThisFromHolder();
        super.remove();
    }

    private void superRemove() {
        super.remove();
    }
}

步驟說明:

  1. 在代碼中,作者構建了一個holder對象,這個對象是一個InheritableThreadLocal , 里面的類型是一個弱引用的WeakHashMap , 這個map的va lu就是TransmittableThreadLocal , 至于value永遠都是空的

holder里面存儲的是這個應用里面,所有關于TransmittableThreadLocal的引用。

  1. 從上面可以看到,每次get, set ,remove都會操作holder對象,這樣做的目的是為了保持TransmittableThreadLocal所有的這個引用都在holder里面存一份。

TtlRunnable

回到我們上面的代碼

Future future = poolExecutor.submit(TtlRunnable.get(new Task("任務1")));

細心的朋友可能已經發現了,我們調用了TtlRunnable 對象的get方法,下面看一下這個方法有啥作用吧

public static TtlRunnable get(@Nullable Runnable runnable, boolean releaseTtlValueReferenceAfterRun, boolean idempotent) {
        if (null == runnable) return null;

        if (runnable instanceof TtlEnhanced) {
            // avoid redundant decoration, and ensure idempotency
            if (idempotent) return (TtlRunnable) runnable;
            else throw new IllegalStateException("Already TtlRunnable!");
        }
  			// 重點在這里
        return new TtlRunnable(runnable, releaseTtlValueReferenceAfterRun);
    }

看上面的代碼,細節上我們不看,我們看大致的思路, 這個地方主要就是根據傳入的runnable構建了一個TtlRunnable對象。

private TtlRunnable(@NonNull Runnable runnable, boolean releaseTtlValueReferenceAfterRun) {
  			//重點在這里
        this.capturedRef = new AtomicReference(capture());
        this.runnable = runnable;
        this.releaseTtlValueReferenceAfterRun = releaseTtlValueReferenceAfterRun;
}

下面這行代碼,運行到這里的時候,還是在主線程里面,調用了capture方法

this.capturedRef = new AtomicReference(capture());
capture
public static Object capture() {
    // 構建一個臨時對象,主要看captureTtlValues方法
    return new Snapshot(captureTtlValues(), captureThreadLocalValues());
}

private static WeakHashMap, Object> captureTtlValues() {
  // 構建一個WeakHashMap方法,
  WeakHashMap, Object> ttl2Value = new WeakHashMap, Object>();
  // 在主線程里面,調用holder變量,循環獲取里面所有的key和value
  for (TransmittableThreadLocal threadLocal : holder.get().keySet()) {
    ttl2Value.put(threadLocal, threadLocal.copyValue());
  }
  // 返回出去
  return ttl2Value;
}

步驟說明:

1.調用靜態變量holder, 循環獲取里面所有的key和value, value的獲取就比較巧妙一點。

private T copyValue() {
      // 這里的get方法,調用的是父類的方法,可以在父類里面最終獲取到當前TransmittableThreadLocal所對應的value
      return copy(get());
}

2.組裝好一個WeakHashMap出去,最終就會到了我們上面的構造方法里面,針對capturedRef 的賦值操作。

run
@Override
public void run() {
  //1. 獲取到剛剛構造TtlRunnable對象的時候初始化的capturedRef對象。包含了從submit丟任務進來的時候父線程的數據
  Object captured = capturedRef.get();
  if (captured == null || releaseTtlValueReferenceAfterRun && !capturedRef.compareAndSet(captured, null)) {
    throw new IllegalStateException("TTL value reference is released after run!");
  }
	// 清除不在captured里面的key,同時在這個子線程中,對所有的ThreadLocal進行重新設置值
  Object backup = replay(captured);
  try {
    // 執行實際的線程方法
    runnable.run();
  } finally {
    // 做好還原工作,根據backup
    restore(backup);
  }
}

private static WeakHashMap, Object> replayTtlValues(@NonNull WeakHashMap, Object> captured) {
            WeakHashMap, Object> backup = new WeakHashMap, Object>();

    for (final Iterator> iterator = holder.get().keySet().iterator(); iterator.hasNext(); ) {
      TransmittableThreadLocal threadLocal = iterator.next();

      // 做好當前線程的local備份
      backup.put(threadLocal, threadLocal.get());

      // 清除數據,不在captured里面的。
      if (!captured.containsKey(threadLocal)) {
        iterator.remove();
        threadLocal.superRemove();
      }
    }

    // 這里就是把值設置到當前線程的TransmittableThreadLocal里面。
    setTtlValuesTo(captured);

    // 一個鉤子
    doExecuteCallback(true);

    return backup;
}

到此,關于“TransmittableThreadLocal怎么解決池化復用線程的傳值問題”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注創新互聯網站,小編會繼續努力為大家帶來更多實用的文章!


網頁標題:TransmittableThreadLocal怎么解決池化復用線程的傳值問題
標題來源:http://www.yuzhuanjia.cn/article/ppdgjd.html 主站蜘蛛池模板: 波多野结衣家庭教师诱惑 | 高潮尖叫免费视频 | 成人啪精品视频网站午夜 | 成人福利| av电影大全五月天 | 91久久精品国产免费一区 | 午夜精品在线观看 | 99久久无码一区人妻A黑国产馆 | 午夜a级理论片在线播放西瓜 | 一区二区三区免费视频播放器 | 91福利精品导航完整版电影在线观看 | 午夜片无码区在线观看视频 | 99久久无色码中文字幕 | 97精品伊人久久大香线蕉 | 99久久亚洲日本精品 | 91大神精品长腿在线 | 99久久国产成人免费网站 | www欧美无国产精选尤物 | 丁香午夜 | 91狠狠色综合久久久夜色撩人 | 91国在线产高清视频 | 91嫩草欧美久久久九九九 | av网站在线嫩草影院 | 国产v片在线观看 | 97久久精品人人槡人妻人小说下载电影久久人人爽天天玩人 | 国产aa夜夜欢一级黄色片 | 91超碰成人在线 | 高潮射精日本韩国在线 | 午夜福利92国语 | 爆乳上司julia中文字幕 | 国产1区2区在线观看 | 99久无码中文字幕 | hd最新国产人妖ts视频 | 动漫精品一区二区无码 | 午夜三级a三级三点窝 | 国产av网站一区二 | 99精品国产高清一区二区三区香蕉 | 99久久精品无码一区二区毛色欲 | av站天堂资 | 韩国午夜理伦三级在线观看仙踪林 | 国产 欧美在线观看 |