2016-12-23 10 views
1
import java.util.concurrent.Executors 
import scala.concurrent.{ExecutionContext, Future} 

object TestInheritableThreadLocal { 

    def main(args: Array[String]): Unit = { 

    implicit val ec = ExecutionContext.fromExecutor(Executors.newFixedThreadPool(2)) 

    val tl: InheritableThreadLocal[String] = new InheritableThreadLocal[String]() 
    tl.set("InitialValue") 

    Future { 
     println("111 " + Thread.currentThread() + tl.get()) 
     Future { 
     println("222 " + Thread.currentThread() + tl.get()) 
     } 
    } 
    Thread.sleep(3000) 

    Future { 
     tl.set("NewInitialValue") 
     println("333 " + Thread.currentThread() + tl.get()) 
     Future { 
     println("444 " + Thread.currentThread() + tl.get()) 
     } 
     Thread.sleep(3000) 
    } 
    } 
} 

ВыходныхInheritableThreadLocal значения не наследуется ExecutorService нитей

111 Thread[pool-1-thread-1,5,main]InitialValue 
222 Thread[pool-1-thread-2,5,main]InitialValue 
333 Thread[pool-1-thread-1,5,main]NewInitialValue 
444 Thread[pool-1-thread-2,5,main]InitialValue 

я ожидал «NewInitialValue» в последней строке вывода, так как 333 резьбы породивших ниток 444 и ТЛ является Наследуемым нить локальным.

В чем причина этой проблемы и как ее можно решить?

ответ

5

Вы не должны полагаться на InheritableThreadLocal, если у вас нет контроля над созданием потоков. В Javadoc состояния:

[...], когда создается дочерний поток, ребенок получает начальные значения для все наследуемые нит локальных переменных, для которых родитель имеет значения.

В вашем примере, потоки создаются с помощью ExecutorService возвращенного Executors.newFixedThreadPool(2)

Это исполнителя, который будет использовать до двух потоков для выполнения ваших задач. Из Javadoc

Создает пул потоков, который повторно использует фиксированное количество потоков, работающих выкл общей неограниченной очереди. В любой момент не более nThreads темы будут активными задачами обработки. Если дополнительные задачи отправляются , когда все потоки активны, они будут ждать в очереди до тех пор, пока не появится поток .

Это деталь реализации, но эти потоки создаются лениво, по мере необходимости. Когда вы отправляете первую задачу, 111, вызов submit создаст и начнет новую тему. Этот новый поток наследует значение InitialValue. Аналогично, когда этот поток отправляет вторую задачу, 222, ее вызов submit заставит создать второй поток, который также наследует InitialValue.

Затем отправьте третью задачу, 333, перепишите значение InheritableThreadLocal и распечатайте его. Когда вы отправляете четвертую задачу 444, ExecutorService использует существующие потоки для ее выполнения. Этот поток уже имеет значение, унаследованное ранее.

, как она может быть решена

Это трудно ответить, не зная, что вы хотите сделать. Но, если вы хотите эффективно использовать InheritableThreadLocal, все сводится к пониманию и контролю создания потоков и, следовательно, цепи наследования.

Вы можете создать и использовать ExecutorService, который создает и использует новый поток для каждой отправленной задачи, например.

Аналогичным образом вы можете использовать другой механизм для распространения этого значения: AtomicReference или лямбда-захват неизменяемого значения.

0

Если вы посмотрите на имена потоков на выходе, вы увидите, что есть два потока (согласно вашей конфигурации ExecutionContext) pool-1-thread-1 и pool-1-thread-2.

444 нить является повторно используемой резьбой pool-1-thread-2, которая ранее использовалась 222 с уже назначенным tl.

333 нити повторное использование pool-1-thread-1 который ранее использовавшийся 111 с tl уже назначены, но это перезапись унаследованного InitialValue к NewInitialValue.

Вы можете увидеть другой выход, если вы увеличите значение no. потоков. Это то, что я получаю с

три нитей:

111 Thread[pool-1-thread-1,5,main]InitialValue 
222 Thread[pool-1-thread-2,5,main]InitialValue 
333 Thread[pool-1-thread-3,5,main]NewInitialValue 
444 Thread[pool-1-thread-2,5,main]InitialValue // Reusing "pool-1-thread-2" 

четыре темы:

111 Thread[pool-1-thread-1,5,main]InitialValue 
222 Thread[pool-1-thread-2,5,main]InitialValue 
333 Thread[pool-1-thread-3,5,main]NewInitialValue 
444 Thread[pool-1-thread-4,5,main]NewInitialValue // Fresh thread "pool-1-thread-4" 
0

Я использую Spring Integration и использование исполнителя для обработки разделения сообщений. В то же время столкнулась с той же проблемой.

@Soritos прав

Это трудно ответить, не зная, что вы хотите сделать

Что обходной путь я сделал

  1. Добавлена ​​ThreadLocal переменная в MessageHeaders.
  2. В Splitter сообщение, создал новый InhertiableThreadLocal и присвоить значение из MessageHeaders

    if (null != message.getHeaders().get("frameworkCorrelationID")) { 
        private static final InheritableThreadLocal<String> id = new InheritableThreadLocal(); 
        id.set((String)message.getHeaders().get("frameworkCorrelationID")); }