2015-11-25 2 views
8

Я пытаюсь выяснить, как я могу скопировать значение ThreadLocal в параллельном потоке Java 8.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 

Есть ли способ для меня, чтобы клонировать 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» в первую очередь, а не просто создан в вашем основном методе, а затем явно передан лямбда? –

+4

Вы пытаетесь пересечь потоки? Я слышал, что это будет плохо. – zapl

ответ

7

As Louis has stated in the comments, ваш пример может быть очень хорошо, чтобы уменьшить захватывая значение локальной переменной в лямбда-выражения

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 обеспечить наследования значений от родительской нити к дочернему потоку : при создании дочернего потока дочерний элемент получает начальные значения для всех наследуемых нитевых локальных переменных , для которых родитель имеет значения.

В вашем случае, объявляя val как InheritableThreadLocal, так как Thread экземпляры, созданные для parallel() в ForkJoinPool#commonPool() созданы лениво, все они будут наследовать от значения set в main метода (и нити).

Это не было бы дело, если вы каким-то образом используется commonPool (или любой другой бассейн операции parallel терминал был вызван), прежде чем установить значение InhertiableThreadLocal в происхождении потока.

+0

Да, мой пример не дал слишком много информации о прецеденте. Фактический прецедент связан с каким-то другим кодом (кроме потребителя parallel()), а ThreadLocal заполняется в сервлет-фильтре, а затем используется в Spring MVC-контроллере и несколько раз клонируется для различных исполняемых реализаций по всей доске (сервисные вызовы и т. д.). Таким образом, на дороге будут различные вызовы, которые являются ThreadContext.get (что-то). Спасибо за объяснение InheritableThreadLocal, я попробую! –