1. 程式人生 > >多執行緒之 Final變數 詳解

多執行緒之 Final變數 詳解

原文:

http://www.tuicool.com/articles/2Yjmqy

併發程式設計網:http://ifeve.com/java-memory-model/

總結:

Final 變數在併發當中,原理是通過禁止cpu的指令集重排序(重排序詳解http://ifeve.com/java-memory-model-1/ http://ifeve.com/java-memory-model-2/),來提供現成的課件性,來保證物件的安全釋出,防止物件引用被其他執行緒在物件被完全構造完成前拿到並使用。

與前面介紹的鎖和volatile相比較,對final域的讀和寫更像是普通的變數訪問。對於final域,編譯器和處理器要遵守兩個重排序規則:

  1. 在建構函式內對一個final域的寫入,與隨後把這個被構造物件的引用賦值給一個引用變數,這兩個操作之間不能重排序。
  2. 初次讀一個包含final域的物件的引用,與隨後初次讀這個final域,這兩個操作之間不能重排序。

與Volatile 有相似作用,不過Final主要用於不可變變數(基本資料型別和非基本資料型別),進行安全的釋出(初始化)。而Volatile可以用於安全的釋出不可變變數,也可以提供可變變數的可見性。

安全釋出的常用模式

可變物件必須通過安全的方式來發布,這通常意味著在釋出和使用該物件的執行緒時都必須使用同步。現在,我們將重點介紹如何確保使用物件的執行緒能夠看到該物件處於已釋出的狀態,並稍後介紹如何在物件釋出後對其可見性進行修改。

安全地釋出一個物件,物件的應用以及物件的狀態必須同時對其他執行緒可見。一個正確構造的物件可以通過以下方式來安全地釋出:

  • 在靜態初始化函式中初始化一個物件引用
  • 將物件的應用儲存到volatile型別的域或者AtomicReferance物件中
  • 將物件的引用儲存到某個正確構造物件的final型別域中
  • 將物件的引用儲存到一個由鎖保護的域中。

線上程安全容器內部的同步意味著,在將物件放入到某個容器,例如Vector或synchronizedList時,將滿足上述最後一條需求。如果執行緒A將物件X放入一個執行緒安全的容器,隨後執行緒B讀取這個物件,那麼可以確保B看到A設定的X狀態,即便在這段讀/寫X的應用程式程式碼中沒有包含顯式的同步。儘管Javadoc在這個主題上沒有給出很清晰的說明,但執行緒安全庫中的容器類提供了以下的安全釋出保證:

  • 通過將一個鍵或者值放入Hashtable、synchronizedMap或者ConcurrentMap中,可以安全地將它釋出給任何從這些容器中訪問它的執行緒(無論是直接訪問還是通過迭代器訪問)
  • 通過將某個元素放入Vector、CopyOnWriteArrayList、CopyOnWriteArraySet、synchronizedList或synchronizedSet中,可以將該元素安全地釋出到任何從這些容器中訪問該元素的執行緒
  • 通過將某個元素放入BlockingQueue或者ConcurrentLinkedQueue中,可以將該元素安全地釋出到任何從這些佇列中訪問該元素的執行緒。

類庫中的其他資料傳遞機制(例如Future和Exchanger)同樣能實現安全釋出,在介紹這些機制時將討論它們的安全釋出功能。

通常,要釋出一個靜態構造的物件,最簡單和最安全的方式是使用靜態的初始化器: public static Holder holder = new Holder(42);

靜態初始化器由JVM在類的初始化階段執行。由於在JVM內部存在著同步機制,因此通過這種方式初始化的任何物件都可以被安全地釋出[JLS 12.4.2]。

詳解如下:

一、不變性

滿足同步需求的另一種方法是使用不可變物件(Immutable Object)。到目前為止,我們介紹了許多與原子性和可見性相關的問題,例如得到失效資料,丟失更新操作或光查到某個物件處於不一致的狀態等等,都與多執行緒檢視同時訪問同一個可變的狀態相關。如果物件的狀態不會改變,那麼這些問題與複雜性也就自然消失了。

如果某個物件在被建立後其狀態就不能被修改,那麼這個物件就被成為不可變物件。執行緒安全型是不可變物件的固有屬性之一,他們的不變性條件是由 建構函式建立的,只要他們的狀態不改變,那麼這些不變性條件就能得以維持。

不可變物件很簡單。他們只有一種狀態,並且該 狀態由建構函式來控制 。在程式設計中一個最困難的地方就是判斷複雜物件的可能狀態。然而,判斷不可變物件的狀態卻很簡單。

雖然在 Java 規範和 Java 記憶體模型中都沒有給出不可變性的正式定義,但不可變性並不等於將物件中所有的域都宣告為 final 型別,即使物件中所有的域都是 final 型別的,這個物件也仍然是可變的,因為在 final 型別的域中可以儲存對可變物件的引用。

當滿足以下條件時,物件才是不可變的:

  • 物件建立完之後其狀態就不能修改
  • 物件的所有與都是 final 型別
  • 物件時正確建立的(建立期間沒有 this 的逸出)

我們來分析下面這個類。

@Immutable
public final class ThreeStooges {
  private final Set<String> stooges = new HashSet<String>();

  public ThreeStooges() {
    stooges.add("Moe");
    stooges.add("Larry");
    stooges.add("Curly");
  }

  public boolean isStooge(String name) {
    return stooges.contains(name);
  }
}

在不可變物件的內部仍可以使用可變物件來管理它們的狀態,如 ThreeStooges 所示。儘管儲存姓名的Set物件是可變的,但從ThreeStooges的設計中可以看到,在Set物件構造完成後無法對其進行修改。stooges是一個final型別的引用變數,因此所有的物件狀態都通過一個final域來訪問。最後一個要求是“正確地構造物件”,這個要求很容易滿足,因為建構函式能使該引用由除了建構函式及其呼叫者之外的程式碼來訪問。

由於程式的狀態總在不斷地變化,你可能會認為需要使用不可變物件的地方不多,但實際情況並非如此。在“不可變的物件”與“不可變的物件引用”之間存在著差異。儲存在不可變物件中的程式狀態仍然可以更新,即通過將一個儲存新狀態的例項來“替換”原有的不可變物件。

Final 域

關鍵字 final 可以視為 C++ 中 const 機制的一種受限版本,用於構造不可變物件。final 型別的域是不能修改的(但如果 final 域所引用的物件時可變的,那麼這些被引用的物件是可以修改的)。然而,在 Java 記憶體模型中,final 域還有著特殊的語義。final 域能確保初始化過程的安全性,從而可以不受限制的訪問不可變物件,並在共享這些物件時無需同步。

注: 個人理解為,final 欄位一旦被初始化完成,並且構造器沒有把 this 引用傳遞出去,那麼在其他執行緒中就能看到 final 欄位的值(域內變數可見性,和 volatile 類似),而且其外部可見狀態永遠也不會改變。它所帶來的安全性是最簡單最純粹的。

注: 即使物件是可變的,通過將物件的某些域宣告為final型別,仍然可以 簡化對狀態的判斷 ,因此限制物件的可變性也就相當於限制了該物件可能的狀態集合。僅包含一個或兩個可變狀態的“基本不可變”物件仍然比包含多個可變狀態的物件簡單。通過將域宣告為final型別,也相當於告訴維護人員這些域是不會變化的。

正如“除非需要更高的可見性,否則應將所有的餓域都宣告為私有域”[EJ Item 12]是一個良好的變成習慣,“除非需要某個域是可變的,否則應將其宣告為final域”也是一個良好的變成習慣。

示例:使用 Volatile 型別來發布不可變物件

之前我們講過, volatile 可以用來保證域的可見性而不能保證變數操作的原子性,更為準確的講,只能保證讀寫操作具有原子性,而不能保證自增 i++ 等運算操作的原子性。

在前面的UnsafeCachingFactorizer類中,我們嘗試用兩個AtomicReferences變數來儲存最新的數值及其因數分解結果,但這種方式並非是執行緒安全的,因為我們無法以原子方式來同時讀取或更新這兩個相關的值。同樣,用volatile型別的變數來儲存這些值也不是執行緒安全的。然而,在某些情況下,不可變物件能提供一種弱形式的原子性。

因式分解Servlet將執行兩個原子操作:更新快取的結果,以及通過判斷快取中的數值是否等於請求的數值來決定是否直接讀取快取中的因數分解結果。每當需要對一組相關資料以原子方式執行某個操作時,就可以考慮建立一個不可變的類來包含這些資料,例如 OneValueCache。

@Immutable
class OneValueCache {
  private final BigInteger lastNumber;
  private final BigInteger[] lastFactors;

  /**
   * 如果在建構函式中沒有使用 Arrays.copyOf()方法,那麼域內不可變物件 lastFactors卻能被域外程式碼改變
   * 那麼 OneValueCache 就不是不可變的。
   */
  public OneValueCache(BigInteger i,
             BigInteger[] factors) {
    lastNumber  = i;
    lastFactors = Arrays.copyOf(factors, factors.length);
  }

  public BigInteger[] getFactors(BigInteger i) {
    if (lastNumber == null || !lastNumber.equals(i))
      return null;
    else
      return Arrays.copyOf(lastFactors, lastFactors.length);
  }
}
        對於在訪問和更新多個相關變數時出現的競爭條件問題,可以通過將這些變數全部儲存在一個不可變物件中來消除。如果是一個可變的物件,那麼就必須使用鎖來確保原子性。如果是一個不可變物件,那麼當執行緒獲得了對該物件的引用後,就 不必擔心另一個執行緒會修改物件的狀態

。如果要更新這些變數,那麼可以建立一個新的容器物件,但其他使用原有物件的執行緒仍然會看到物件處於一致的狀態。

在 VolatileCachedFactorizer使用了OneValueCache來儲存快取的數值及其因數。我們將 OneValueCache 宣告為 volatile,這樣當一個執行緒將cache設定為引用一個新的OneValueCache時,其他執行緒就會立即看到新快取的資料。

@ThreadSafe
public class VolatileCachedFactorizer implements Servlet {
  private volatile OneValueCache cache =
    new OneValueCache(null, null);

  public void service(ServletRequest req, ServletResponse resp) {
    BigInteger i = extractFromRequest(req);
    BigInteger[] factors = cache.getFactors(i);
    if (factors == null) {
      factors = factor(i);
      cache = new OneValueCache(i, factors);//宣告為 volatile ,防止指令重排序,保證可見性
    }
    encodeIntoResponse(resp, factors);
  }
}

與cache相關的操作不會相互干擾,因為OneValueCache是不可變的,並且在每條相應的程式碼路徑中只會訪問它一次。通過使用包含多個狀態變數的容器物件來維持不變性條件,並使用一個volatile型別的引用來確保可見性,使得Volatile Cached Factorizer在沒有顯式地使用鎖的情況下仍然是執行緒安全的。

二、安全釋出

到目前為止,我們重點討論的是如何確保物件不被髮布,例如讓物件封閉線上程或另一個物件的內部。當然,在某些情況下我們希望在多個執行緒間共享物件,此時必須確保安全地進行共享。然而,如果只是像下面程式那樣將物件引用儲存到公有域中,那麼還不足以安全地釋出這個物件。

//不安全的釋出
public Holder holder;

public void initialize() {
    holder = new Holder(42);
}

你可能會奇怪,這個看似沒有問題的示例何以會執行失敗。由於存在可見性問題,其他執行緒看到的Holder物件將處於不一致的狀態,即便在該物件的建構函式中已經正確地構建了不變性條件。這種不正確的釋出導致其他執行緒看到尚未建立完成的物件。

不正確的釋出:正確的物件被破壞

你不能指望一個尚未被完全建立的物件擁有完整性。某個觀察該物件的執行緒將看到物件處於不一致的狀態,然後看到物件的狀態突然發生變化,即使執行緒在物件釋出後還沒有修改過它。事實上,如果下面程式中的Holder使用前面程式中的不安全釋出方式,那麼另一個執行緒在呼叫assertSanity時將丟擲AssertionError。

public class Holder {
  private int n;

  public Holder(int n) { this.n = n; }

  public void assertSanity() {
    if (n != n)
      throw new AssertionError("This statement is false.");
  }
}

由於沒有使用同步來確保Holder物件對其他執行緒可見,因此將Holder稱為“未被正確釋出”。在未被正確釋出的物件中存在兩個問題。

首先 ,除了釋出物件的執行緒外,其他執行緒可以看到的 Holder域是一個失效值 ,因此將看到一個空引用或者之前的舊值。

 然而 ,更糟糕的情況是,執行緒看到Holder引用的值是最新的,但Holder狀態的值卻是失效的。情況變得更加不可預測的是,某個執行緒在第一次讀取域時得到失效值,而再次讀取這個域時會得到一個更新值,這也是assertSainty丟擲AssertionError的原因。

如果沒有足夠的同步,那麼當在多個執行緒間共享資料時將發生一些非常奇怪的事情。

不可變物件與初始化安全性

由於不可變物件是一種非常重要的物件,因此 Java記憶體模型為不可變物件的共享提供了一種特殊的初始化安全性保證 。我們已經知道,即使某個物件的引用對其他執行緒是可見的,也並不意味著物件狀態對於使用該物件的執行緒來說一定是可見的。為了確保物件狀態能呈現出一致的檢視,就必須使用同步。

另一方面,即使在釋出不可變物件的引用時沒有使用同步,也仍然可以安全地訪問該物件。為了維持這種初始化安全性的保證,必須滿足不可變性的所有需求:狀態不可修改,所有域都是final型別,以及正確的構造過程。( 如果Holder物件是不可變的,那麼即使Holder沒有被正確地釋出,在assertSanity中也不會丟擲AssertionError。)

任何執行緒都可以在不需要額外同步的情況下安全地訪問不可改變物件,即使在釋出這些物件時沒有使用同步。

這種保證還將延伸到被正確建立物件中所有final型別的域。在沒有額外同步的情況下,也可以安全地訪問final型別的域。然而,如果final型別的域所指向的是可變物件,那麼在訪問這些域所指向的物件的狀態時仍然需要同步。

安全釋出的常用模式

可變物件必須通過安全的方式來發布,這通常意味著在釋出和使用該物件的執行緒時都必須使用同步。現在,我們將重點介紹如何確保使用物件的執行緒能夠看到該物件處於已釋出的狀態,並稍後介紹如何在物件釋出後對其可見性進行修改。

安全地釋出一個物件,物件的應用以及物件的狀態必須同時對其他執行緒可見。一個正確構造的物件可以通過以下方式來安全地釋出:

  • 在靜態初始化函式中初始化一個物件引用
  • 將物件的應用儲存到volatile型別的域或者AtomicReferance物件中
  • 將物件的引用儲存到某個正確構造物件的final型別域中
  • 將物件的引用儲存到一個由鎖保護的域中。

線上程安全容器內部的同步意味著,在將物件放入到某個容器,例如Vector或synchronizedList時,將滿足上述最後一條需求。如果執行緒A將物件X放入一個執行緒安全的容器,隨後執行緒B讀取這個物件,那麼可以確保B看到A設定的X狀態,即便在這段讀/寫X的應用程式程式碼中沒有包含顯式的同步。儘管Javadoc在這個主題上沒有給出很清晰的說明,但執行緒安全庫中的容器類提供了以下的安全釋出保證:

  • 通過將一個鍵或者值放入Hashtable、synchronizedMap或者ConcurrentMap中,可以安全地將它釋出給任何從這些容器中訪問它的執行緒(無論是直接訪問還是通過迭代器訪問)
  • 通過將某個元素放入Vector、CopyOnWriteArrayList、CopyOnWriteArraySet、synchronizedList或synchronizedSet中,可以將該元素安全地釋出到任何從這些容器中訪問該元素的執行緒
  • 通過將某個元素放入BlockingQueue或者ConcurrentLinkedQueue中,可以將該元素安全地釋出到任何從這些佇列中訪問該元素的執行緒。

類庫中的其他資料傳遞機制(例如Future和Exchanger)同樣能實現安全釋出,在介紹這些機制時將討論它們的安全釋出功能。

通常,要釋出一個靜態構造的物件,最簡單和最安全的方式是使用靜態的初始化器: public static Holder holder = new Holder(42);

靜態初始化器由JVM在類的初始化階段執行。由於在JVM內部存在著同步機制,因此通過這種方式初始化的任何物件都可以被安全地釋出[JLS 12.4.2]。

事實不可變物件

如果物件在釋出後不會被修改,那麼對於其他在沒有額外同步的情況下安全地訪問這些物件的執行緒來說,安全釋出是足夠的。所有的安全釋出機制都能確保,當物件的引用對所有訪問該物件的執行緒可見時,物件釋出時的狀態對於所有執行緒也將是可見的,並且如果物件狀態不會再改變,那麼就足以確保任何訪問都是安全的。

如果物件從技術上來看是可變的,但其狀態在釋出後不會再改變,那麼把這種物件稱為“ 事實不可變物件 (Effectively Immutable Object)”。這些物件不需要滿足之前提出的不可變性的嚴格定義。在這些物件釋出後,程式只需將它們視為不可變物件即可。通過使用事實不可變物件,不僅可以簡化開發過程,而且還能由於減少了同步而提高效能。

在沒有額外的同步的情況下,任何執行緒都可以安全地使用被安全釋出的事實不可變物件。

例如,Date本身是可變的,但如果將它作為不可變物件來使用,那麼在多個執行緒之間共享Date物件時,就可以省去對鎖的使用。假設需要維護一個Map物件,其中儲存了每位使用者的最近登入時間: public Map<String, Date> lastLogin =Collections.synchronizedMap(new HashMap<String, Date>());

如果Date物件的值在被放入Map後就不會改變,那麼synchronizedMap中的同步機制就足以使Date值被安全地釋出,並且在訪問這些Date值時不需要額外的同步。

可變物件

如果物件在構造後可以修改,那麼安全釋出只能確保“釋出當時”狀態的可見性。對於可變物件,不僅在釋出物件時需要使用同步,而且在每次物件訪問時同樣需要使用同步來確保後續修改操作的可見性。要安全地共享可變物件,這些物件就必須被安全地釋出,並且必須是執行緒安全的或者由某個鎖保護起來。

物件的釋出需求取決於它的可變性:

  • 不可變物件可以通過任意機制來發布
  • 事實不可改變必須通過安全方式釋出
  • 可變物件必須通過安全方式釋出,並且必須是執行緒安全的或者由某個鎖保護起來

安全的共享物件

當獲得物件的一個引用時,你需要知道在這個引用上可以執行哪些操作。在使用它之前是否需要獲得一個鎖?是否可以修改它的狀態,或者只能讀取它?許多併發錯誤都是由於沒有理解共享物件的這些“既定規則”而導致的。當釋出一個物件時,必須明確地說明物件的訪問方式。

相關推薦

執行 Final變數

原文: http://www.tuicool.com/articles/2Yjmqy 併發程式設計網:http://ifeve.com/java-memory-model/ 總結: Final 變數在併發當中,原理是通過禁止cpu的指令集重排序(重排序詳解http://ifeve.com/java

執行Future使用

什麼是Future Future是一個未來物件,裡面儲存這執行緒處理結果,它像一個提貨憑證,拿著它你可以隨時去提取結果 什麼時候使用 在兩種情況下,離開Future幾乎很難辦。 一種情況是拆分訂單,比如你的應用收到一個批量訂單,此時如果要求最快的處理訂單,那麼需要併發

Android 執行HandlerThread 完全

  之前對執行緒也寫過幾篇文章,不過倒是沒有針對android,因為java與android線上程方面大部分還是相同,不過本篇我們要介紹的是android的專屬類HandlerThread,因為HandlerThread在設定思想上還是挺值得我們學習的,那麼我們下面來

執行重排序

重排序 重排序是指編譯器和處理器為了優化程式效能而對指令序列進行重新排序的一種手段。 資料依賴性 如果兩個操作訪問同一個變數,且這兩個操作中有一個為寫操作,此時這兩個操作之間就存在資料的依賴性。資料依賴分為3中型別,如下表所示: 上面3中情況,只要重排序兩個操作的順序。程式的結

Java 執行synchronized關鍵字

package com.example; /** * Created by 晁東洋 on 2017/5/27. */ public class MyThreadClass { public static void main(String args[]){ Exampletest

Java執行Condition介面原理

Condition介面提供了類似Object的監視器方法,與Lock配合可以實現等待/通知模式,但是這兩者在使用方式以及功能特性上還是有差別的 Condition介面詳解 Condition定義了等待/通知兩種型別的方法,當前執行緒呼叫這些方法時,需要提前獲

執行原子變數CAS演算法(二)

上篇博文,我們介紹了多執行緒之記憶體可見性Volatile(一),但是也遺留了一個問題,如何保證變數的”原子性操作(Atomic operations)”? Volatile保證部分型別的原子性 上篇博文,我們說Voloatile不能保證原子性,有一點侷

C#基礎系列:執行的常見用法

前言:此篇就主要從博主使用過的幾種多執行緒的用法從應用層面大概介紹下。文中觀點都是博主個人的理解,如果有不對的地方望大家指正~~ 1、多執行緒:使用多個處理控制代碼同時對多個任務進行控制處理的一種技術。據博主的理解,多執行緒就是該應用的主執行緒任命其他多個執行緒去協

Java執行同步和非同步

1. 多執行緒併發時,多個執行緒同時請求同一資源,必然導致此資源的資料不安全。 2. 執行緒池 在WEB服務中,對於web伺服器的響應速度必須儘可能的快,這就容不得在使用者提交請求按鈕後,再建立執行緒提供服務。為了減少使用者的等待時間,執行緒必須預先建立,放線上程池中,執行

java執行共享變數

目的:簡述java多執行緒的共享變數   共享變數:多個執行緒都會使用到的同一變數。   Q : 為什麼共享變數會造成資料的錯誤呢???       A : 多個執行緒在操作共享變數的時候,不是直接在主記憶體中去操作的。而

入坑JAVA執行併發(八)ThreadLocal使用和原理

  ThreadLocal是一個用於儲存多執行緒變數的類,它可以把執行緒與設定的值對應起來,因為它為變數在每個執行緒都建立了一個副本。訪問的時候每個執行緒只能訪問到自己的副本變數。 例項 看如下程式碼: public class Main {

C#基礎系列——執行的常見用法

前言:前面幾節分別介紹了下C#基礎技術中的反射、特性、泛型、序列化、擴充套件方法、Linq to Xml等,這篇跟著來介紹下C#的另一基礎技術的使用。最近專案有點緊張,所以準備也不是特別充分。此篇就主要從博主使用過的幾種多執行緒的用法從應用層面大概介紹下。文中觀點都是博主個人的理解,如果有不對的地方望大家指正

Java執行volatile變數

Java 語言中的 volatile 變數可以被看作是一種 “程度較輕的 synchronized”;與 synchronized 塊相比,volatile 變數所需的編碼較少,並且執行時開銷也較少,但是它所能實現的功能也僅是 synchronized 的一部分。本文介紹了幾種有效使用 volati

JAVA執行Thread VS Runnable

要求 必備知識 本文要求基本瞭解JAVA程式設計知識。 開發環境 windows 7/EditPlus 演示地址 原始檔 程序與執行緒 程序是程式在處理機中的一次執行。一個程序既包括其所要執行的指令,也包括了執行指令所需的系統資源,不同程序所

java執行與併發程式設計

一、多執行緒1、作業系統有兩個容易混淆的概念,程序和執行緒。程序:一個計算機程式的執行例項,包含了需要執行的指令;有自己的獨立地址空間,包含程式內容和資料;不同程序的地址空間是互相隔離的;程序擁有各種資源和狀態資訊,包括開啟的檔案、子程序和訊號處理。執行緒:表示程式的執行流程

Python程序與執行程式設計及GIL

介紹如何使用python的multiprocess和threading模組進行多執行緒和多程序程式設計。 Python的多程序程式設計與multiprocess模組 python的多程序程式設計主要依靠multiprocess模組。我們先對比兩段程式碼,看看多程序程式設計的優勢。我們模擬了一個非常耗時的任

Java執行執行排程

排程的概念 給定時間結點,時間間隔,任務次數,然後自動執行任務 應用場景舉例 1.日誌清理:每隔三個月,清理公司日誌檔案 2.日誌備份:每個一週,備份公司檔案 3.工資結算:每個月29號,考勤彙報,業務結算,計算工資 排程的實現方式:

Android執行Java 8中ThreadLocal內部實現機制

前言:ThreadLocal是執行緒內部的儲存類,通過它可以實現在每個執行緒中儲存自己的私有資料。即資料儲存以後,只能在指定的執行緒中獲取這個儲存的物件,而其它執行緒則不能獲取到當前執行緒儲存的這個物件。ThreadLocal有一個典型的應用場景,即我們在前文中

Java執行fork/join框架

這個框架的目的主要是更好地利用底層平臺上的多核CPU和多處理器來進行處理,解決問題時通常使用分治演算法或map/reduce演算法來進行.這個框架的名稱來源於使用時的兩個基本操作fork和join,可以類比於map/reduce中的map和reduce操作.fork操作的作

Java執行隔離技術ThreadLocal原始碼

本篇文章是對ThreadLocal和InheritableThreadLocal,TransmittableThreadLocal的原理和原始碼進行深入分析,並舉例講解,其中前兩個是JDK自帶的。原理相對比較簡單,其解決了單執行緒環境和在單執行緒中又建立執行緒(