Java面试题必备知识之ThreadLocal (3)

ThreadLocal对象的生命周期跟线程的生命周期一样长,那么如果将ThreadLocal对象和线程池一起使用,就可能会遇到这种情况:一个线程的ThreadLocal对象会和其他线程的ThreadLocal对象串掉,一般不建议将两者一起使用。

案例学习 Dubbo中对ThreadLocal的使用

我从Dubbo中找到了ThreadLocal的例子,它主要是用在请求缓存的场景,具体代码如下:

@Activate(group = {Constants.CONSUMER, Constants.PROVIDER}, value = Constants.CACHE_KEY) public class CacheFilter implements Filter { private CacheFactory cacheFactory; public void setCacheFactory(CacheFactory cacheFactory) { this.cacheFactory = cacheFactory; } @Override public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException { if (cacheFactory != null && ConfigUtils.isNotEmpty(invoker.getUrl().getMethodParameter(invocation.getMethodName(), Constants.CACHE_KEY))) { Cache cache = cacheFactory.getCache(invoker.getUrl(), invocation); if (cache != null) { String key = StringUtils.toArgumentString(invocation.getArguments()); Object value = cache.get(key); if (value != null) { if (value instanceof ValueWrapper) { return new RpcResult(((ValueWrapper)value).get()); } else { return new RpcResult(value); } } Result result = invoker.invoke(invocation); if (!result.hasException()) { cache.put(key, new ValueWrapper(result.getValue())); } return result; } } return invoker.invoke(invocation); }

可以看出,在RPC调用(invoke)的链路上,会先使用请求参数判断当前线程是否刚刚发起过同样参数的调用——这个调用会使用ThreadLocalCache保存起来。具体的看,ThreadLocalCache的实现如下:

package org.apache.dubbo.cache.support.threadlocal; import org.apache.dubbo.cache.Cache; import org.apache.dubbo.common.URL; import java.util.HashMap; import java.util.Map; /** * ThreadLocalCache */ public class ThreadLocalCache implements Cache { //ThreadLocal里存放的是参数到结果的映射 private final ThreadLocal<Map<Object, Object>> store; public ThreadLocalCache(URL url) { this.store = new ThreadLocal<Map<Object, Object>>() { @Override protected Map<Object, Object> initialValue() { return new HashMap<Object, Object>(); } }; } @Override public void put(Object key, Object value) { store.get().put(key, value); } @Override public Object get(Object key) { return store.get().get(key); } } RocketMQ

在RocketMQ中,我也找到了ThreadLocal的身影,它是用在消息发送的场景,MQClientAPIImpl是RMQ中负责将消息发送到服务端的实现,其中有一个步骤需要选择一个具体的队列,选择具体的队列的时候,不同的线程有自己负责的index值,这里使用了ThreadLocal的机制,可以看下ThreadLocalIndex的实现:

package org.apache.rocketmq.client.common; import java.util.Random; public class ThreadLocalIndex { private final ThreadLocal<Integer> threadLocalIndex = new ThreadLocal<Integer>(); private final Random random = new Random(); public int getAndIncrement() { Integer index = this.threadLocalIndex.get(); if (null == index) { index = Math.abs(random.nextInt()); if (index < 0) index = 0; this.threadLocalIndex.set(index); } index = Math.abs(index + 1); if (index < 0) index = 0; this.threadLocalIndex.set(index); return index; } @Override public String toString() { return "ThreadLocalIndex{" + "threadLocalIndex=" + threadLocalIndex.get() + '}'; } } 总结

这篇文章主要是解决了关于ThreadLocal的几个问题:(1)具体的概念是啥?(2)在Java开发中的什么场景下使用?(3)ThreadLocal的实现原理是怎样的?(4)开源项目中有哪些案例可以参考?不知道你是否对这几个问题有了一定的了解呢?如果还有疑问,欢迎交流。

参考资料

Why 0x61c88647?

Java ThreadLocal

When and how should I use a ThreadLocal variable?

技术小黑屋:理解Java中的ThreadLocal

深入分析ThreadLocal的内存泄漏问题

《Java并发编程实战》

InheritableThreadLocal详解

ThreadLocal详解

ThreadLocal的使用场景

数据结构:哈希表

本号专注于后端技术、JVM问题排查和优化、Java面试题、个人成长和自我管理等主题,为读者提供一线开发者的工作和成长经验,期待你能在这里有所收获。

javaadu

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zyyfyy.html