一款基于过滤器的线程共享变量的清理机制

项目中常常用到线程共享变量。如多个函数或对象之间传递参数。循环读取缓存或数据库时时用共享变量减少读取次数。某类特殊对象的持有等。一般用finally去强制释放共享变量。但释放时机有时并不能准确的把握。为此,基于过滤器写了个个释放机制。

过滤器如下:

public class ThreadLocalClearFilter implements Filter {

    @AutowiredFalse
    private List<ThreadLocalCleaner> threadLocalCleaners;

    @Override
    public 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 {
    
    @Override
    public void cleanThreadLocal() {
        DictNameSharer.clear();
    }
}

看上去挺多的。但还算稳定,没有发生过内存泄漏事故。可能会有其他更好的机制。本文仅做参考。

你可能感兴趣的:(java,java-ee)