1. 程式人生 > >慕課網實戰·高併發探索(七):執行緒封閉

慕課網實戰·高併發探索(七):執行緒封閉

特別感謝:慕課網jimin老師的《Java併發程式設計與高併發解決方案》課程,以下知識點多數來自老師的課程內容。
jimin老師課程地址:Java併發程式設計與高併發解決方案

1、什麼是執行緒封閉?

它其實就是把物件封裝到一個執行緒裡,只有一個執行緒能看到這個物件,那麼這個物件就算不是執行緒安全的,也不會出現任何執行緒安全方面的問題。

執行緒封閉技術有一個常見的應用:

資料庫連線對應jdbc的Connection物件,Connection物件在實現的時候並沒有對執行緒安全做太多的處理,jdbc的規範裡也沒有要求Connection物件必須是執行緒安全的。
實際在伺服器應用程式中,執行緒從連線池獲取了一個Connection物件,使用完再把Connection物件返回給連線池,由於大多數請求都是由單執行緒採用同步的方式來處理的,並且在Connection物件返回之前,連線池不會將它分配給其他執行緒。因此這種連線管理模式處理請求時隱含的將Connection物件封閉線上程裡面,這樣我們使用的connection物件雖然本身不是執行緒安全的,但是它通過執行緒封閉也做到了執行緒安全。

2、執行緒封閉的種類:

(1)Ad-hoc 執行緒封閉:

Ad-hoc執行緒封閉是指,維護執行緒封閉性的職責完全由程式實現來承擔。Ad-hoc執行緒封閉是非常脆弱的,因為沒有任何一種語言特性,例如可見性修飾符或區域性變數,能將物件封閉到目標執行緒上。事實上,對執行緒封閉物件(例如,GUI應用程式中的視覺化元件或資料模型等)的引用通常儲存在公有變數中。

(2)堆疊封閉:
堆疊封閉其實就是方法中定義區域性變數。不存在併發問題。多個執行緒訪問一個方法的時候,方法中的區域性變數都會被拷貝一份到執行緒的棧中(Java記憶體模型),所以區域性變數是不會被多個執行緒所共享的。

(3)ThreadLocal執行緒封閉:
它是一個特別好的封閉方法,其實ThreadLocal內部維護了一個map,map的key是每個執行緒的名稱,而map的value就是我們要封閉的物件。ThreadLocal提供了get、set、remove方法,每個操作都是基於當前執行緒的,所以它是執行緒安全的。
這裡寫圖片描述

    //ThreadLocal的get方法原始碼
    public T get() {
        Thread t = Thread.currentThread();//當前執行緒物件
        ThreadLocalMap map = getMap(t);//get操作基於當前執行緒
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked"
) T result = (T)e.value; return result; } } return setInitialValue(); }

3、Springboot框架中使用ThreadLocal

說明:
1、這裡不描述springboot框架的搭建過程,假定你已經有了一個可以正常執行的springboot簡單專案。
2、我們這裡的例子使用的是springboot框架中的filter與Interceptor來使用threadLocal,對於Springboot的filter與Interceptor不做過多的講解。

coding:
(1)建立一個包含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();
    }
}

(2)建立Filter,在Filter中對ThreadLocal做新增操作。

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;
        //列印當前執行緒的ID與請求路徑
        log.info("do filter, {}, {}", Thread.currentThread().getId(), request.getServletPath());
        //將當前執行緒ID新增到ThreadLocal中
        RequestHolder.add(Thread.currentThread().getId());
        filterChain.doFilter(servletRequest, servletResponse);
    }

    @Override
    public void destroy() {

    }
}

(3)建立controller,在controller中獲取到filter中存入的值

@Controller
@RequestMapping("/threadLocal")
public class ThreadLocalController {

    @RequestMapping("/test")
    @ResponseBody
    public Long test() {
        return RequestHolder.getId();
    }
}

(4)建立攔截器Interceptor,在攔截器中刪除剛才新增的值

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;
    }
}

(5)在springboot的啟動類Application中註冊filter與Interceptor。要繼承WebMvcConfigurerAdapter 類。(我這裡的啟動類名為:ConcurrencyApplication)

@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());
        //設定要過濾的url
        registrationBean.addUrlPatterns("/threadLocal/*");
        return registrationBean;
    }

    //註冊攔截器
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new HttpInterceptor()).addPathPatterns("/**");
    }
}
  • 頁面中打印出我們當前的執行緒ID:
    這裡寫圖片描述

  • 檢視控制檯:
    這裡寫圖片描述
    從控制檯的列印日誌我們可以看出,首先filter過濾器先獲取到我們當前的執行緒ID為40、我們當前的請求路徑為/threadLocal/test ,緊接著進入了我們的Interceptor的preHandle方法中,列印了preHandle字樣。最後進入了我們的Interceptor的afterCompletion方法,刪除了我們之前存入的值,並列印了afterCompletion字樣。

特別感謝:慕課網jimin老師的《Java併發程式設計與高併發解決方案》課程,以上知識點多數來自老師的課程內容。
jimin老師課程地址:Java併發程式設計與高併發解決方案

相關推薦

實戰·併發探索執行封閉

特別感謝:慕課網jimin老師的《Java併發程式設計與高併發解決方案》課程,以下知識點多數來自老師的課程內容。 jimin老師課程地址:Java併發程式設計與高併發解決方案 1、什麼是執行緒封閉? 它其實就是把物件封裝到一個執行緒裡,只有一個執行緒能

實戰·併發探索執行安全性-可見性-有序性

可見性 什麼是可見性? 一個執行緒對主記憶體的修改可以及時的被其他執行緒觀察到 導致共享變數線上程間不可見的原因 執行緒交叉執行 重排序結合線程交叉執行 共享變數更新後的值沒有在工作記憶體與主存間及時更新 JVM處理可見性 J

實戰·併發探索併發容器 J.U.C

特別感謝:慕課網jimin老師的《Java併發程式設計與高併發解決方案》課程,以下知識點多數來自老師的課程內容。 jimin老師課程地址:Java併發程式設計與高併發解決方案 概述 Java併發容器JUC是三個單詞的縮寫。是JDK下面的一個包名。

實戰·併發探索十四執行池 Executor

特別感謝:慕課網jimin老師的《Java併發程式設計與高併發解決方案》課程,以下知識點多數來自老師的課程內容。 jimin老師課程地址:Java併發程式設計與高併發解決方案 new Thread的弊端 每次new Thread 新建物件,效能

實戰·併發探索十二併發容器J.U.C -- AQS元件 鎖ReentrantLock、ReentrantReadWriteLock、StempedLock

特別感謝:慕課網jimin老師的《Java併發程式設計與高併發解決方案》課程,以下知識點多數來自老師的課程內容。 jimin老師課程地址:Java併發程式設計與高併發解決方案 ReentrantLock java中有兩類鎖,一類是Synchron

Java併發程式設計2執行中斷含程式碼

使用interrupt()中斷執行緒當一個執行緒執行時,另一個執行緒可以呼叫對應的Thread物件的interrupt()方法來中斷它,該方法只是在目標執行緒中設定一個標誌,表示它已經被中斷,並立即返回。這裡需要注意的是,如果只是單純的呼叫interrupt()方法,執行緒並沒有實際被中斷,會繼續往下執行。

Java併發程式設計3執行掛起、恢復與終止的正確方法含程式碼

JAVA大資料中高階架構 2018-11-06 14:24:56掛起和恢復執行緒Thread 的API中包含兩個被淘汰的方法,它們用於臨時掛起和重啟某個執行緒,這些方法已經被淘汰,因為它們是不安全的,不穩定的。如果在不合適的時候掛起執行緒(比如,鎖定共享資源時),此時便可能會發生死鎖條件——其他執行緒在等待該

併發程式設計執行基礎、執行之間的共享與協作

一、基礎概念 1.1 CPU核心數、執行緒數 **兩者的關係:**cpu的核心數與執行緒數是1:1的關係,例如一個8核的cpu,支援8個執行緒同時執行。但在intel引入超執行緒技術以後,cpu與執行緒數的關係就變成了1:2。此外在開發過程中並沒感覺到執行緒的限制,那是因為cpu

執行池原理執行池的使用

這篇文章通過介紹執行緒池的常見用法,總結前面學習到的內容。 主要包括 ThreadPoolExecutor的使用 ScheduledThreadPoolExecutor的使用 ExecutorCompletionService的使用 1. 統計某個區間

eXosip入門執行操作

       osip2/eXosip2支援執行緒安全,既可以用於多執行緒的程式設計模式,也可以用於單執行緒的程式設計模式。osip2/eXosip2預設是使用多執行緒模式,也就是預設使能OSIP_MT巨集。        當使用多執行緒模式時,使用者需要編寫執行緒處理函式

2017.4.26 --Java 併發秒殺API

Java高併發秒殺API系列(一)                  -----------------業務分析及Dao層 第一章 課程介紹 1.1 內容介紹及業務分析 (1)課程內容 1 SSM框架的整合使用 2 秒殺類系統需求理解和實現 3 常用技術解決高併發問題 (

-java併發秒殺api之併發優化-總結

1.架構優化 2.spring宣告式事務 宣告式事務:http://www.open-open.com/lib/view/open1414310646012.html 配置並使用Spring宣告式事務 在spring-service.xml中新增上配置事務管理器 <

】JavaScript中OOP

1.概念與繼承 面向物件程式設計(Object-oriented programming,OOP)是一種程式設計範型,同時也是一種程式開發的方法。物件指的是類的例項。它將物件作為程式的基本單元,將程式和資料封裝其中,以提高軟體的重用性、靈活性和擴充套件性。 面向物件特點:繼承、封

Java併發程式設計Java併發工具類

1. 等待多執行緒完成的CountDownLatch CountDownLatch允許一個或多個執行緒等待其他執行緒完成操作。 1.1 應用場景 假如有這樣一個需求:我們需要解析一個Excel裡多個sheet的資料,此時可以考慮使用多 執行緒,每個執行緒解析一個sheet裡的資料

Java併發程式設計Java併發容器和框架

1. ConcurrentHashMap 1.1 ConcurrentHashMap的優勢 在併發程式設計中使用HashMap可能導致程式死迴圈。而使用執行緒安全的HashTable效率又非 常低下,基於以上兩個原因,便有了ConcurrentHashMap的登場機會。

Java併發程式設計Java執行

目錄 一、執行緒池概念 二、執行緒池狀態 三、Excecutors四種建立執行緒池方法 四、Java中的ThreadPoolExecutor類 五、執行緒執行流程 六、一個簡單的執行緒池實現 一、執行緒池概念 一種執行緒使用模式。執行緒過多會帶來排程開銷,進而影響

Java併發程式設計執行四種實現方式

Java實現多執行緒的方式 Java實現多執行緒的方式有4種: 繼承Thread方法、實現Runnable介面、實現Callable介面並通過FutureTask建立執行緒、使用ExecutorService。 其中,前兩種執行緒執行結果沒有返回值,後兩種是有返回值的。 1、繼承Th

併發第十四彈執行池的介紹及使用

單執行緒就不說了因為簡單,並且 在實際的生產環境中一般必須來說 執行緒資源都是由執行緒池提供執行緒資源的。 執行緒池的好處 重用存在的執行緒,減少物件建立、消亡的開銷,效能好 可有效控制最大併發執行緒數,提高系統資源利用率,同時可以避免過多資源競爭,避免阻塞。 提供定時執行、定期執行、單執行緒、併發數控制等

併發集合使用執行安全的、帶有延遲元素的列表

宣告:本文是《 Java 7 Concurrency Cookbook 》的第六章,作者: Javier Fernández González     譯者:許巧輝 校對:方騰飛 使用執行緒安全的、帶有延遲元素的列表 DelayedQueue類是Java API提供的一種有趣的資料結構,並且你

併發集合使用執行安全的NavigableMap

宣告:本文是《 Java 7 Concurrency Cookbook 》的第六章,作者: Javier Fernández González     譯者:許巧輝 校對:方騰飛 使用執行緒安全的NavigableMap Java API 提供的有趣的資料結構,並且你可以在併發應用程式中使用