4 回答

TA贡献1812条经验 获得超5个赞
将 ThreadLocal 视为由同一线程执行的代码的某种“内存缓存”。完全相同的线程。在不同线程上执行的代码之间共享 ThreadLocal 是一个坏主意。
Tha javadoc 明确指出:
此类提供线程局部变量。这些变量不同于它们的正常对应变量,因为每个访问一个(通过它的 get 或 set 方法)的线程都有它自己的、独立初始化的变量副本。ThreadLocal 实例通常是希望将状态与线程相关联的类中的私有静态字段(例如,用户 ID 或事务 ID)。
换句话说:使用 ThreadLocals 的目标是为在不同线程中运行的“每个”代码提供“线程特定”数据。
另一方面,ExecutorService 首先是一个接口:您根本不知道它是由单个线程提供支持,还是(更可能)由多个线程提供支持。
换句话说:使用 ExecutorService 会很快导致多个不同的线程运行您的 Runnables/Tasks。然后您将在这些多个线程中共享您的 ThreadLocal。
因此,“危险”可能是错误的词。使用 ThreadLocal的目标是拥有每个线程的存储,而 ExecutorService 是关于由未知数量的线程执行的代码。这两件事根本不能很好地结合在一起。
重点是不同的:一个概念强调与非常具体的“活动”相关的长期存在的线索。另一个概念是关于使用未知数量的无名线程执行小型独立活动。

TA贡献2003条经验 获得超2个赞
需要注意的是,您的多次运行Runnable
可能会在不同的线程上执行。执行器服务可以由单个线程支持,但也可以由线程池支持。在您的后续执行中Runnable
,不同的线程将访问不同的ThreadLocal
.
因此,您当然可以在ThreadLocal
. Runnable
但它不太可能有用,因为通常 a 的目的ThreadLocal
是保持一个值一段时间。相反,aRunnable
通常应该是短暂的。
所以,不,通常将 aThreadLocal
与线程池一起使用是没有意义的。

TA贡献1848条经验 获得超10个赞
ThreadLocal 将产生不确定的结果——因为我们不能保证给定 userId 的每个 Runnable 操作在每次执行时都由同一个线程处理。
在发布的代码示例中,上面的参数是无效的,因为该ThreadLocal
值是在run()
被调用时设置的,因此同一块中的任何后续get()
都是确定性的,无论使用ExecutorService
.
然后从另一个调用不是确定性set(new Context())
的,因为您无法控制正在执行的。Runnable A
get()
Runnable B
Thread
Runnable
假设返回的对象get()
可以是任何东西,除非你知道它最后一次设置的时间。

TA贡献1860条经验 获得超9个赞
ThreadLocal 用于在设置变量后在该线程中缓存变量。所以下次你想访问它时,你可以直接从 ThreadLocal 中获取它,而无需初始化。
由于您设置它threadLocal.set(obj)
并通过线程内访问它threadLocal.get()
,因此您直接获得线程安全保证。
但是,如果您不明确清除缓存,事情可能会变得丑陋threadLocal.remove()
。
在线程池中,排队的任务会被线程一一处理,大部分时间任务应该是独立的,但是线程范围缓存threadLocal如果忘记清除它会使后面的任务依赖于它之前的任务首先在处理下一个任务之前;
缓存的threadLocals 不会立即被 gc-ed(在某个未知的时刻 - 超出您的控制),因为它们的键是WeakReference,这可能会在您不知情的情况下导致 OOM。
remove()
一个简单的演示,用于未显式调用导致 OOM的这种情况。
public class ThreadLocalOne {
private static final int THREAD_POOL_SIZE = 500;
private static final int LIST_SIZE = 1024 * 25;
private static ThreadLocal<List<Integer>> threadLocal = new ThreadLocal<>();
public static void main(String[] args) throws InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(THREAD_POOL_SIZE);
for (int i = 0; i < THREAD_POOL_SIZE; i++) {
executorService.execute(() -> {
threadLocal.set(getBigList());
System.out.println(Thread.currentThread().getName() + " : " + threadLocal.get().size());
// threadLocal.remove();
// explicitly remove the cache, OOM shall not occur;
});
}
executorService.shutdown();
}
private static List<Integer> getBigList() {
List<Integer> ret = new ArrayList<>();
for (int i = 0; i < LIST_SIZE; i++) {
ret.add(i);
}
return ret;
}
}
添加回答
举报