internal class ThreadLocalTest { @Test fun `test thread local corruption`() { val howManyTimes = 10 val executorService = Executors.newFixedThreadPool(10) val futures: MutableList> = mutableListOf() repeat(howManyTimes) { i -> val context = Context(i, executorService) futures.add(executorService.submit(context)) } futures.forEach { future -> future.get() } } class Context(private val input: Int, private val executorService: ExecutorService) : Runnable { private val threadLocal = ThreadLocal() fun get(): Int? = threadLocal.get() override fun run() { threadLocal.set(input) println("Set on ${Thread.currentThread()} a thread local with value: '${threadLocal.get()}'") if (input % 2 == 0) { runBlocking { val jobs = mutableListOf() repeat(10) { val job = launch(executorService.asCoroutineDispatcher()) { println("Launch start, current thread: ${Thread.currentThread()}, thread local value should be '$input' and it's: '${threadLocal.get()}'") yield() println("After yield, current thread: ${Thread.currentThread()}, thread local value should be '$input' and it's: '${threadLocal.get()}'") } jobs.add(job) } jobs.joinAll() } } println("Context on ${Thread.currentThread()} before sleeping: '${threadLocal.get()}'") Thread.sleep(input.toLong() * 1000) println("Context on ${Thread.currentThread()} after sleeping: '${threadLocal.get()}'") } } }