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)
}
}
}
输出
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 Thread生成了Thread 444,而tl是一个可继承的本地线程。
导致此问题的原因是什么以及如何解决?
答案 0 :(得分:10)
当您无法控制线程的创建时,您不应该依赖InheritableThreadLocal
。 javadoc声明:
[...]创建子线程时,子进程接收初始值 父所具有的所有可继承的线程局部变量 值。
在您的示例中,线程由Executors.newFixedThreadPool(2)
ExecutorService
创建
这是一个执行程序,它将使用最多两个线程来执行您的任务。来自javadoc
创建一个重用固定数量的线程的线程池 关闭共享的无界队列。在任何时候,最多
nThreads
个线程 将是主动处理任务。如果提交了其他任务 当所有线程都处于活动状态时,它们将在队列中等待直到a 线程可用。
这是一个实现细节,但这些线程是根据需要延迟创建的。当您提交第一个任务111
时,对submit
的调用将创建并启动一个新线程。这个新线程将继承值InitialValue
。类似地,当此线程提交第二个任务222
时,其对submit
的调用将强制创建第二个线程,该线程也将继承InitialValue
。
然后,您提交第三个任务333
,覆盖InheritableThreadLocal
的值并打印出来。当您提交第四个任务444
时,ExecutorService
使用现有线程来执行它。该线程已经有一个值,早先继承了。
如何解决
如果不知道自己想做什么,很难回答。但是,如果你想有效地使用InheritableThreadLocal
,那么这一切都归结为了解和控制线程的创建,因此也就是继承链。
例如,您可以创建并使用为每个提交的任务创建和使用新线程的ExecutorService
。
类似地,您可以使用另一种机制来传播该值:AtomicReference
或不可变值的lambda捕获。
答案 1 :(得分: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
覆盖为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"
}。
如果增加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-4,5,main]NewInitialValue // Fresh thread "pool-1-thread-4"
四个主题:
select q.project from Status q
where q.name = 'new'
and q.id in (select max(sq.id) from Status sq group by sq.project.id)
答案 2 :(得分:0)
我正在使用Spring Integration并使用执行程序来处理拆分消息。 在这期间面临同样的问题。
@Soritos是对的
在不知道自己想做什么的情况下很难回答
我做了哪些解决方法
在消息Splitter中,创建了一个新的InhertiableThreadLocal并从MessageHeaders中分配值
if (null != message.getHeaders().get("frameworkCorrelationID")) {
private static final InheritableThreadLocal<String> id = new InheritableThreadLocal();
id.set((String)message.getHeaders().get("frameworkCorrelationID")); }