慕課網實戰·高併發探索(七):執行緒封閉
特別感謝:慕課網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 提供的有趣的資料結構,並且你可以在併發應用程式中使用