2015-11-25 69 views
8

我想弄清楚如何在Java 8並行流中複製ThreadLocal值。Java 8並行流和ThreadLocal

因此,如果我們可以這樣考慮:

public class ThreadLocalTest { 

     public static void main(String[] args) { 
      ThreadContext.set("MAIN"); 
      System.out.printf("Main Thread: %s\n", ThreadContext.get()); 

      IntStream.range(0,8).boxed().parallel().forEach(n -> { 
       System.out.printf("Parallel Consumer - %d: %s\n", n, ThreadContext.get()); 
      }); 
     } 

     private static class ThreadContext { 
      private static ThreadLocal<String> val = ThreadLocal.withInitial(() -> "empty"); 

      public ThreadContext() { 
      } 

      public static String get() { 
       return val.get(); 
      } 

      public static void set(String x) { 
       ThreadContext.val.set(x); 
      } 
     } 
    } 

,輸出

Main Thread: MAIN 
Parallel Consumer - 5: MAIN 
Parallel Consumer - 4: MAIN 
Parallel Consumer - 7: empty 
Parallel Consumer - 3: empty 
Parallel Consumer - 1: empty 
Parallel Consumer - 6: empty 
Parallel Consumer - 2: empty 
Parallel Consumer - 0: MAIN 

有我從main()方法ThreadLocal的克隆到其產卵每個並行線程的方式執行?

這樣使我的結果是:

Main Thread: MAIN 
Parallel Consumer - 5: MAIN 
Parallel Consumer - 4: MAIN 
Parallel Consumer - 7: MAIN 
Parallel Consumer - 3: MAIN 
Parallel Consumer - 1: MAIN 
Parallel Consumer - 6: MAIN 
Parallel Consumer - 2: MAIN 
Parallel Consumer - 0: MAIN 

,而不是第一個?

+3

我不希望如此。但是爲什麼這個'ThreadLocal'放在第一位,而不是僅僅在你的main方法中創建,然後顯式地傳遞給lambda? –

+4

你想穿過溪流嗎?我聽說它會很糟糕。 – zapl

回答

7

As Louis has stated in the comments,你的例子可以很好地減少到lambda表達式

public static void main(String[] args) { 
    String value = "MAIN"; 
    System.out.printf("Main Thread: %s\n", value); 

    IntStream.range(0,8).boxed().parallel().forEach(n -> { 
     System.out.printf("Parallel Consumer - %d: %s\n", n, value); 
    }); 
} 

這不是明顯的從你的例子充分利用的情況下是什麼捕獲一個局部變量的值。

如果你知道線程會正是從你的主線程開始,您可以考慮使用InheritableThreadLocal

此類擴展ThreadLocal提供繼承值從 父線程到子線程 :當創建子線程時, 子項會接收父項具有值的所有可繼承線程局部變量 的初始值。

在你的情況下,宣佈valInheritableThreadLocal,因爲ForkJoinPool#commonPool()內爲parallel()創建Thread情況下懶洋洋地創建,他們都從main方法(和線程)的值set繼承。

這不會是這樣的,如果你以某種方式使用commonPool(或任何池parallel終端操作要求),設置在原點線程InhertiableThreadLocal前值。

+0

是的,我的示例沒有提供太多關於用例的信息。實際的用例與其他一些代碼一起(parallel()消費者除外),並且ThreadLocal被填充到一個Servlet過濾器中,然後被用於Spring MVC控制器,並且有時克隆用於各種可運行的實現服務電話等)。所以會有不同的調用ThreadContext.get(something)。感謝您解釋InheritableThreadLocal,我會試試看! –