最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

springboot 使用ThreadLocal的實例代碼

 更新時間:2021年12月06日 15:13:30   作者:沒錯,是一只菜鳥啦  
這篇文章主要介紹了springboot 使用ThreadLocal的實例代碼,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教

springboot 使用ThreadLocal

本文參考慕課教程給出一個在spring boot中使用ThreadLocal實現(xiàn)線程封閉的實例。

首先創(chuàng)建一個包含ThreadLocal成員變量的實例:

public class RequestHolder { 
    private final static ThreadLocal<Long> requestHolder = new ThreadLocal<>();
    public static void add(Long id) {
        requestHolder.set(id);
    }
 
    public static Long getId() {
        return requestHolder.get();
    }
 
    public static void remove() {
        requestHolder.remove();
    }
}

編寫一個Controller類,請求該類的test()方法獲取ThreadLocal中存儲的id:

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
 
@Controller
@RequestMapping("/threadLocal")
public class ThreadLocalController {
 
    @RequestMapping("/test")
    @ResponseBody
    public Long test() {
        return RequestHolder.getId();
    }
}

編寫過濾器,在請求到達(dá)Servlet之前(請求->tomcat容器->filter->servlet->inteceptor->controller),將當(dāng)前線程的id添加到ThreadLocal中:

import com.mmall.concurrency.example.threadLocal.RequestHolder;
import lombok.extern.slf4j.Slf4j; 
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
 
@Slf4j
public class HttpFilter implements Filter {
 
    @Override
    public void init(FilterConfig filterConfig) throws ServletException { 
    }
    
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        log.info("do filter, {}, {}", Thread.currentThread().getId(), request.getServletPath());
        //在ThreadLocal中添加當(dāng)前線程的id
        RequestHolder.add(Thread.currentThread().getId());
        filterChain.doFilter(servletRequest, servletResponse);
    } 
    @Override
    public void destroy() { 
    }
}

編寫攔截器,當(dāng)請求處理完成后(從Controller返回后),清除ThreadLocal中的id,避免內(nèi)存泄漏。

import com.mmall.concurrency.example.threadLocal.RequestHolder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; 
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
@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 {
        log.info("ThreadId:"+RequestHolder.getId());
        RequestHolder.remove();
        log.info("afterCompletion");
        return;
    }
}

最后,我們需要在spring boot啟動類上注冊我們定義的Filer及Inteceptor,并設(shè)置攔截路徑。

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
 
@SpringBootApplication
public class ConcurrencyApplication extends WebMvcConfigurerAdapter{ 
 public static void main(String[] args) {
  SpringApplication.run(ConcurrencyApplication.class, args);
 }
 
 @Bean
 public FilterRegistrationBean httpFilter() {
  FilterRegistrationBean registrationBean = new FilterRegistrationBean();
  registrationBean.setFilter(new HttpFilter());
  registrationBean.addUrlPatterns("/threadLocal/*");
  return registrationBean;
 }
 
 @Override
 public void addInterceptors(InterceptorRegistry registry) {
  registry.addInterceptor(new HttpInterceptor()).addPathPatterns("/**");
 }
}

在瀏覽器或者postman中輸入http://localhost:8080/threadLocal/test

觀察輸出結(jié)果:

2018-11-09 11:16:51.287? INFO 34076 --- [?????????? main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8080 (http)
2018-11-09 11:16:51.290? INFO 34076 --- [?????????? main] c.m.concurrency.ConcurrencyApplication?? : Started ConcurrencyApplication in 1.718 seconds (JVM running for 2.132)
2018-11-09 11:17:03.060? INFO 34076 --- [nio-8080-exec-2] o.a.c.c.C.[Tomcat].[localhost].[/]?????? : Initializing Spring FrameworkServlet 'dispatcherServlet'
2018-11-09 11:17:03.060? INFO 34076 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet??????? : FrameworkServlet 'dispatcherServlet': initialization started
2018-11-09 11:17:03.072? INFO 34076 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet??????? : FrameworkServlet 'dispatcherServlet': initialization completed in 12 ms
2018-11-09 11:17:03.078? INFO 34076 --- [nio-8080-exec-2] com.mmall.concurrency.HttpFilter???????? : do filter, 29, /threadLocal/test
2018-11-09 11:17:03.090? INFO 34076 --- [nio-8080-exec-2] com.mmall.concurrency.HttpInterceptor??? : preHandle
2018-11-09 11:17:03.124? INFO 34076 --- [nio-8080-exec-2] com.mmall.concurrency.HttpInterceptor??? : ThreadId:29
2018-11-09 11:17:03.124? INFO 34076 --- [nio-8080-exec-2] com.mmall.concurrency.HttpInterceptor??? : afterCompletion

從打印的日志結(jié)果中,我們看到在Filter中我們將當(dāng)前線程的id 29添加到了ThreadLocal中,隨后在Inteceptor中打印并刪除了id。

ThreadLocal在springboot使用中的坑

ThreadLocal 適用于變量在線程間隔離,而在方法或類間共享的場景?,F(xiàn)在在Springboot中我做如下場景的使用:

使用 Spring Boot 創(chuàng)建一個 Web 應(yīng)用程序,使用 ThreadLocal 存放一個 Integer 的值,來暫且代表需要在線程中保存的用戶信息,這個值初始是 null。在業(yè)務(wù)邏輯中,我先從 ThreadLocal 獲取一次值,然后把外部傳入的參數(shù)設(shè)置到 ThreadLocal 中,來模擬從當(dāng)前上下文獲取到用戶信息的邏輯,隨后再獲取一次值,最后輸出兩次獲得的值和線程名稱。

@RestController
public class threadLocal {
    private ThreadLocal<Integer> currentUser = ThreadLocal.withInitial(() -> null);
    @RequestMapping("wrong")
    public Map wrong(@RequestParam("userId") Integer userId) {
        //設(shè)置用戶信息之前先查詢一次ThreadLocal中的用戶信息
        String before = Thread.currentThread().getName() + ":" + currentUser.get();
        //設(shè)置用戶信息到ThreadLocal
        currentUser.set(userId);
      
            //設(shè)置用戶信息之后再查詢一次ThreadLocal中的用戶信息
            String after = Thread.currentThread().getName() + ":" + currentUser.get();
            //匯總輸出兩次查詢結(jié)果
            Map result = new HashMap();
            result.put("before", before);
            result.put("after", after);
            return result;      
    }
}

為了讓問題快速的重現(xiàn),我在配置文件中設(shè)置一下 Tomcat 的參數(shù),把工作線程池最大線程數(shù)設(shè)置為 1,這樣始終是同一個線程在處理請求:

server.tomcat.max-threads=1

運(yùn)行程序后先讓用戶 1 來請求接口,可以看到第一和第二次獲取到用戶 ID 分別是 null 和 1,符合預(yù)期:隨后用戶 2 來請求接口,這次就出現(xiàn)了 Bug,第一和第二次獲取到用戶 ID 分別是 1 和 2,顯然第一次獲取到了用戶 1 的信息,原因就是 Tomcat 的線程池重用了線程。

在 Tomcat 這種 Web 服務(wù)器下跑的業(yè)務(wù)代碼,本來就運(yùn)行在一個多線程環(huán)境中,并不能認(rèn)為沒有顯式開啟多線程就不會有線程安全問題,所以使用類似 ThreadLocal 工具來存放一些數(shù)據(jù)時,需要特別注意在代碼運(yùn)行完后,顯式地去清空設(shè)置的數(shù)據(jù)。如果在代碼中使用了自定義的線程池,也同樣會遇到這個問題。修改后代碼如下:

@RestController
public class threadLocal {
    private ThreadLocal<Integer> currentUser = ThreadLocal.withInitial(() -> null);
    @RequestMapping("wrong")
    public Map wrong(@RequestParam("userId") Integer userId) {
        //設(shè)置用戶信息之前先查詢一次ThreadLocal中的用戶信息
        String before = Thread.currentThread().getName() + ":" + currentUser.get();
        //設(shè)置用戶信息到ThreadLocal
        currentUser.set(userId);
        try {
            //設(shè)置用戶信息之后再查詢一次ThreadLocal中的用戶信息
            String after = Thread.currentThread().getName() + ":" + currentUser.get();
            //匯總輸出兩次查詢結(jié)果
            Map result = new HashMap();
            result.put("before", before);
            result.put("after", after);
            return result;
        } finally {
         //增加移除處理
            currentUser.remove();
        }
    }
}

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • ssm項目實現(xiàn)用戶登陸持久化(token)

    ssm項目實現(xiàn)用戶登陸持久化(token)

    這篇文章主要介紹了ssm項目實現(xiàn)用戶登陸持久化(token),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-04-04
  • Jrebel License Server 激活 IDEA-Jrebel-在線-離線-均適用(推薦)

    Jrebel License Server 激活 IDEA-Jrebel-在線-

    這篇文章主要介紹了Jrebel License Server 激活 IDEA-Jrebel-在線-離線-均適用,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-12-12
  • Springboot源碼 AbstractAdvisorAutoProxyCreator解析

    Springboot源碼 AbstractAdvisorAutoProxyCreator解析

    這篇文章主要介紹了Springboot源碼 AbstractAdvisorAutoProxyCreator解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2019-08-08
  • Java死鎖_動力節(jié)點Java學(xué)院整理

    Java死鎖_動力節(jié)點Java學(xué)院整理

    死鎖是兩個甚至多個線程被永久阻塞時的一種運(yùn)行局面,這種局面的生成伴隨著至少兩個線程和兩個或者多個資源。在這里我已寫好一個簡單的程序,它將會引起死鎖方案然后我們就會明白如何分析它
    2017-06-06
  • Springboot?maven項目配置文件覆蓋問題的處理

    Springboot?maven項目配置文件覆蓋問題的處理

    這篇文章主要介紹了Springboot?maven項目配置文件覆蓋問題的處理方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-05-05
  • jvm堆外內(nèi)存排查圖文舉例詳解

    jvm堆外內(nèi)存排查圖文舉例詳解

    Java應(yīng)用程序通過直接方式從操作系統(tǒng)中申請的內(nèi)存,叫堆外內(nèi)存,這篇文章主要給大家介紹了關(guān)于jvm堆外內(nèi)存排查的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2023-12-12
  • SpringBoot使用AOP與注解實現(xiàn)請求參數(shù)自動填充流程詳解

    SpringBoot使用AOP與注解實現(xiàn)請求參數(shù)自動填充流程詳解

    面向切面編程(aspect-oriented programming,AOP)主要實現(xiàn)的目的是針對業(yè)務(wù)處理過程中的切面進(jìn)行提取,諸如日志、事務(wù)管理和安全這樣的系統(tǒng)服務(wù),從而使得業(yè)務(wù)邏輯各部分之間的耦合度降低,提高程序的可重用性,同時提高了開發(fā)的效率
    2023-02-02
  • SpringBoot自動重啟的兩種方法

    SpringBoot自動重啟的兩種方法

    我們在項目開發(fā)階段,可能經(jīng)常會修改代碼,修改完后就要重啟Spring Boot,本文主要介紹了SpringBoot自動重啟的兩種方法,具有一定的參考價值,感興趣的可以了解一下
    2023-12-12
  • java應(yīng)用cpu占用過高問題分析及解決方法

    java應(yīng)用cpu占用過高問題分析及解決方法

    這篇文章主要介紹了java應(yīng)用cpu占用過高問題分析及解決方法,具有一定參考價值,需要的朋友可以參考下。
    2017-09-09
  • SpringBoot實現(xiàn)國密SM4加密解密的使用示例

    SpringBoot實現(xiàn)國密SM4加密解密的使用示例

    在商用密碼體系中,SM4主要用于數(shù)據(jù)加密,本文就來介紹一下SpringBoot實現(xiàn)國密SM4加密解密的使用示例,具有一定的參考價值,感興趣的可以了解一下
    2023-10-10

最新評論

冷水江市| 西青区| 枝江市| 苏尼特右旗| 陆川县| 怀化市| 平泉县| 大厂| 清徐县| 丽江市| 浪卡子县| 海淀区| 金沙县| 长垣县| 都兰县| 呼玛县| 息烽县| 江城| 荆门市| 石门县| 中西区| 兰溪市| 永州市| 虞城县| 邹平县| 昆山市| 建德市| 通化县| 朝阳县| 繁峙县| 陕西省| 北流市| 亳州市| 砀山县| 晋州市| 济南市| 南雄市| 武邑县| 息烽县| 原平市| 张家港市|