项目中常常用到线程共享变量。如多个函数或对象之间传递参数。循环读取缓存或数据库时时用共享变量减少读取次数。某类特殊对象的持有等。一般用finally去强制释放共享变量。但释放时机有时并不能准确的把握。为此,基于过滤器写了个个释放机制。
过滤器如下:
public class ThreadLocalClearFilter implements Filter {@AutowiredFalseprivate List<ThreadLocalCleaner> threadLocalCleaners;@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {try {HttpServletRequest httpServletRequest = (HttpServletRequest) request;// 配合ZYRequestUtils.allowCleanThreadLocal使用,// 只有ZYRequestUtils.allowCleanThreadLocal=true的情况下,thread才会必定被清理,// 利用这利确认机制,保障ThreadLocal一定会在当前线程被情况httpServletRequest.setAttribute(ThreadLocalCleaner.CAN_CLEAR_THREAD_FLAG, Thread.currentThread().getName());chain.doFilter(request, response);} finally {if (ZYListUtils.isNotEmptyList(threadLocalCleaners)) {threadLocalCleaners.forEach(ThreadLocalCleaner::cleanThreadLocal);}}}
}public interface ThreadLocalCleaner {String CAN_CLEAR_THREAD_FLAG="can_Clear_Thread_Flag";void cleanThreadLocal();
}
实际使用示例:当循环设置某数据集里面某个字段的字典时,会反复读取redis的某个字典缓存。此时,用共享变量可以让程序只访问一次redis。当前线程内共享该字典。程序设计如下:
public static DictionaryContainer findDictionaryContainer(String dictCode) {// 没有启用字典,字典缓存管理为空if (null == dictCacheManager) {return null;}// 不允许使用共享变量,直接从redis缓存中读if (!ZYRequestUtils.allowCleanThreadLocal()) {return dictCacheManager.findDictionaryContainer(dictCode);}// 能使用共享变量的情况下,从共享变量中获取DictionaryContainer container = DictNameSharer.getContainer(dictCode);if (null != container) {return container;}// 从缓存中读取,并设置进共享变量DictionaryContainer dictionaryContainer = dictCacheManager.findDictionaryContainer(dictCode);DictNameSharer.putContainer(dictCode, dictionaryContainer);return dictionaryContainer;}
// 判断请求头有没有允许设置共享变量的标识,即在过滤器设置的上下文属性。以确认当前线程与过滤器线程是一致的。
public static boolean allowCleanThreadLocal() {HttpServletRequest request = getRequest();if (null == request) {return false;}Object attribute = request.getAttribute(ThreadLocalCleaner.CAN_CLEAR_THREAD_FLAG);String name = Thread.currentThread().getName();// 线程一致的情况下,允许清理线程return null != attribute && ZYBoolUtils.ignoreEquals(attribute, name);}
字典共享变量
public class DictNameSharer {private final static ThreadLocal<Map<String, DictionaryContainer>> CACHE = new ThreadLocal<>();public static DictionaryContainer getContainer(String dictCode) {Map<String, DictionaryContainer> cacheContainer = CACHE.get();return null != cacheContainer ? cacheContainer.get(dictCode) : null;}public static void putContainer(String dictCode, DictionaryContainer container) {if (null == container) {return;}Map<String, DictionaryContainer> dictNameContainer = CACHE.get();dictNameContainer = null != dictNameContainer ? dictNameContainer : new HashMap<>();dictNameContainer.put(dictCode, container);CACHE.set(dictNameContainer);}public static void clear() {CACHE.remove();}
}
ThreadLocal清理。
@Component
public class DependenceThreadLocalCleaner implements ThreadLocalCleaner {@Overridepublic void cleanThreadLocal() {DictNameSharer.clear();}
}
看上去挺多的。但还算稳定,没有发生过内存泄漏事故。可能会有其他更好的机制。本文仅做参考。