您好,登錄后才能下訂單哦!
這篇文章給大家介紹java高并發中如何進行線程封閉,內容非常詳細,感興趣的小伙伴們可以參考借鑒,希望對大家能有所幫助。
很多時候都想躲避并發,避免并發除了設計成不可變對象其實還有一個簡單的方法就是線程封閉。
其實就是把對象封裝到一個線程里,只有這一個線程能看到這個對象。那么這個對象就算不是線程安全的,也不會出現線程安全方面的問題了,因為他只能在一個線程里面進行訪問。那么如何實現線程封閉呢?
Ad-hoc 線程封閉:程序控制實現,最糟糕,忽略
堆棧封閉:局部變量,無并發問題。是我們現實中使用最多的封閉了,簡單說就是局部變量。多個線程訪問一個方法的時候,方法中的局部變量都會被拷貝一份到線程棧中,所以局部變量是不會被線程共享的,因此不會出現并發問題。所以全局變量容易引起并發問題。
ThreadLocal線程封閉:特別好的封閉方法。ThreadLocal內部維護了一個map,key是線程的名稱,value就是封閉的對象。
正常來講我們每一個請求對服務器來講都是一個線程在運行,我們希望線程間隔離,一個線程在被后端服務器實際處理的時候,可以通過Filter過濾器取出當前的用戶,然后將數據存放在ThreadLocal中,當線程被接口的service以及其他相關類進行處理的時候很可能需要在取出當前用戶,這時就可以隨時隨地從ThreadLocal中直接拿到之前存儲的值這樣用起來就很方便了。如果我們不這樣做,會有什么麻煩呢?因為我們的登錄用戶通常是從request中取出來的,因此需要帶上request或者從request中取出來的用戶信息,從controller層開始不停的往下傳,甚至會傳到一些util類中,這樣會使得代碼看起來很臃腫。當使用ThreadLocal和Filter,就可以很方便的在接口處理之前,前取出相關的信息,在接口實際處理的時候,什么時候需要什么時候再把信息取出來,這樣代碼在設計的時候就容易多了,不至于把request從controller一直傳遞下去。
具體使用實例如下:
新建一個類:
public class RequestHolder { //因為當前沒有登錄用戶,我們用線程id來充當 private final static ThreadLocal<Long> requestHolder = new ThreadLocal<>(); /** * 請求進入到后端服務器,但是還沒有實際處理的時候調用add,可以使用Filter * @param id */ public static void add(Long id) { //雖然只傳入id,但是threadLocal會取出當前線程id放到map中的key,value是傳入的值 requestHolder.set(id); } public static Long getId() { return requestHolder.get(); } /** * 如果不做remove的話,會造成內存泄漏,數據永遠不會釋放掉 * 需要在接口真正處理完成之后進行調用,可以使用interceptor */ public static void remove() { requestHolder.remove(); } }
這個類就用來存放ThreadLocal。
新建一個Filter:
@Slf4j public class HttpFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest httpServletRequest = (HttpServletRequest) request; // httpServletRequest.getSession().getAttribute("user"); log.info("do filter , {}, {}", Thread.currentThread().getId(), ((HttpServletRequest) request).getServletPath()); RequestHolder.add(Thread.currentThread().getId()); // 如果這個Filter不想攔截住這個請求,只想做單獨的數據處理時,要調用chain.doFilter,使得攔截器處理完 chain.doFilter(request, response); } @Override public void destroy() { } }
新建一個Interceptor:
@Slf4j public class HttpInterceptor extends HandlerInterceptorAdapter { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { log.info("preHandle"); return true; } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { RequestHolder.remove(); log.info("afterCompletion"); return ; } }
配置filter和interceptor:
@Configuration public class Config implements WebMvcConfigurer { @Bean public FilterRegistrationBean<HttpFilter> httpFilter(){ FilterRegistrationBean<HttpFilter> filterRegistrationBean = new FilterRegistrationBean<>(); // 設置filter filterRegistrationBean.setFilter(new HttpFilter()); // 攔截規則 filterRegistrationBean.addUrlPatterns("/threadLocal/*"); return filterRegistrationBean; } @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new HttpInterceptor()).addPathPatterns("/**"); } }
新建一個controller進行測試:
@RestController @RequestMapping("threadLocal") public class ThreadLocalController { @GetMapping("/test") public Long test() { return RequestHolder.getId(); } }
在瀏覽器中輸入localhost:8080/threadLocal/test可以輸出線程的id,與后臺的輸出id一致。
關于java高并發中如何進行線程封閉就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。