多線程中ThreadLocal的使用

*************************************優雅的分割線 **********************************

分享一波:程序員賺外快-必看的巔峰干貨

如果以上內容對你覺得有用,并想獲取更多的賺錢方式和免費的技術教程

請關注微信公眾號:HB荷包
在這里插入圖片描述
一個能讓你學習技術和賺錢方法的公眾號,持續更新
*************************************優雅的分割線 **********************************
SimpleExecutor

前言

多線程是Java的一個重要特性,多線程從某方面可以等價于多任務,當你有多個任務要處理時,多個任務一起做所消耗的時間肯定比任務串行起來做,所消耗的時間短。而對于多線程不熟悉的新手則容易踩到很多坑,最典型的則是變量問題。

概念介紹

下面先用簡單粗俗的語言解釋一下幾個基本概念

線程安全:多線程訪問時,采用了加鎖機制,當一個線程訪問該類的某個數據時,進行保護,其他線程不能進行訪問直到該線程讀取完,其他線程才可使用。不會出現數據不一致或者數據污染。典型的例子為StringBuffer類。

線程不安全:不提供數據訪問保護,有可能出現多個線程先后更改數據造成所得到的數據是臟數據。典型的例子為StringBuilder類。Servlet和SpringMVC采用的是單例設計模式,因此也是線程不安全的。而aop中如果定義了成員變量,也是線程不安全的。

Java內存模型

參考我之前的文章Java內存模型介紹

結合Java內存模型的介紹可知,在單例模式下,多個線程操作同一個變量,會發生線程安全性問題簡單來說就是一個變量name在線程A中命名為“李鐵蛋”,而線程B將其命名為“李蛋”,此時線程A輸出變量name,極有可能輸出的是“李蛋”。因此,就需要使用ThreadLocal來給每個線程提供局部變量,解決線程安全問題。

示例

首先我們來看一下關于線程不安全的情況

public class test003 implements Runnable {private Res res;public test003(Res res) {this.res = res;}public void run() {for (int i = 0; i < 10; i++) {System.out.println(Thread.currentThread().getName() + "," + res.getNumber());try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}}}public static void main(String[] args) {Res res = new Res();for (int i = 0; i < 4; i++) {new Thread(new test003(res)).start();}}}class Res {public Integer count = 0;public Integer getNumber() {return ++count;}}

程序中的res變量則為主內存中的變量,每個線程都會操作同一個res,獲取到的也是同一個count,因此其中一次運行打印出來的結果如下

Thread-0,1
Thread-1,2
Thread-2,3
Thread-3,4
Thread-0,5
Thread-1,6
Thread-2,7
Thread-3,8
Thread-0,9
Thread-1,10
Thread-2,11
。。。。。

程序的本意為打印每個線程從1開始增長,而運行結果中,比如線程0,第一次為1,第二次為5,很明顯不符合要求,我們將程序部分代碼如下改造:

class Res {public static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>() {@Overrideprotected Integer initialValue() {return 0;}};// 這里其實可以使用JDK8的Lambda表達式簡化代碼// public static ThreadLocal<Integer> threadLocal = ThreadLocal.withInitial(() -> 0);public Integer getNumber() {int count = threadLocal.get() + 1;threadLocal.set(count);return count;}}

改造后的代碼,使用ThreadLocal創建一個成員變量,泛型為Integer,表示這個成員變量為int類型。在getNumber方法中,執行的則是count++操作。threadLocal.get()方法的作用是獲取當前線程threadLocal中的值,+1之后獲取本次的count,并set回去。我們看一下輸出結果。

Thread-0,1
Thread-2,1
Thread-3,1
Thread-1,1
Thread-0,2
Thread-2,2
Thread-3,2
Thread-1,2
Thread-1,3
Thread-0,3
Thread-2,3
Thread-3,3
。。。。

每個線程的結果都是從1開始增長。

總結

ThreadLocal的作用是給每個線程提供局部變量,而這個局部變量就是存儲到工作內存中的。線程之間的局部變量互不影響,達到線程安全的目的。ThreadLocal的應用相當廣泛,如SpringCloud在網關中獲取當前的request,就是使用的ThreadLocal

部分代碼如下:

public class RequestContext extends ConcurrentHashMap<String, Object> {// ThreadLocal存儲RequestContextprotected static final ThreadLocal<? extends RequestContext> threadLocal = new ThreadLocal<RequestContext>() {protected RequestContext initialValue() {try {return (RequestContext)RequestContext.contextClass.newInstance();} catch (Throwable var2) {throw new RuntimeException(var2);}}};// 獲取Request上下文public static RequestContext getCurrentContext() {if (testContext != null) {return testContext;} else {RequestContext context = (RequestContext)threadLocal.get();return context;}}// 獲取當前線程的requestpublic HttpServletRequest getRequest() {return (HttpServletRequest)this.get("request");}}

SpringCloud的源碼我還沒開始看(這玩意源碼太多了估計啃不動),現在在啃Mybatis源碼,因此下面對此的分析只是推測,還希望大佬們不要打我。

Zuul在請求進入后,首先會獲取到request,并將其存儲在RequestContext中,使用threadLocal存儲,可以保證每個線程獲取到的request都是屬于自己的。后續在程序的任意處,都可以使用 RequestContext.getCurrentContext().getRequest() 來獲取當前請求的request對象。

錯誤使用

在web應用中,經常會有人把ThreadLocal作為每個線程的全局變量使用,這種用法是錯誤的。SpringBoot底層有線程池,對于每一個請求,都會從線程池中隨機取出一個線程,因此即使是同一個登錄的用戶,每一次請求都有可能不是同一個線程,而從ThreadLocal中獲取到的值自然也不一樣。關于每次請求都不是同一線程的問題,可以自行打印請求的線程id進行證明,這里就不貼代碼了。

ThreadLocal在web應用中的使用場景為,為每次請求提供一個全局的值,在這一次請求中,可以在任何地方取出來這個值進行操作。如:在aop中解析token獲取登錄中的用戶信息,存放到ThreadLocal,本次請求需要用到登錄用戶的信息,就可以取出來。再如:開發者在aop中記錄日志,代碼全部寫到環繞通知中就顯得冗余,因此獲取ip、參數等內容會寫到前置通知中。而對于要存表的日志,參數在前置通知,返回值在后置通知,報錯信息在環繞通知中,可能會想到把變量定義到最上面,這種寫法也是錯誤的。在上面說過,aop是單例模式,因此這種寫法存在線程安全性問題,在這里就也可以使用ThreadLocal存儲日志信息,最后在后置通知中存表。
*************************************優雅的分割線 **********************************

分享一波:程序員賺外快-必看的巔峰干貨

如果以上內容對你覺得有用,并想獲取更多的賺錢方式和免費的技術教程

請關注微信公眾號:HB荷包
在這里插入圖片描述
一個能讓你學習技術和賺錢方法的公眾號,持續更新
*************************************優雅的分割線 **********************************
SimpleExecutor

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:
http://www.pswp.cn/news/536944.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/536944.shtml
英文地址,請注明出處:http://en.pswp.cn/news/536944.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

mysql 查看所有表的引擎_MySQL查看數據庫、表的占用空間大小以及某個庫中所有表的引擎類型...

本文章來給大家介紹一些常用的MySQL查看數據庫、表的占用空間大小sql命令吧&#xff0c;希望此教程 對各位同學會有所幫助。查看各庫的大小代碼如下復制代碼SELECT SUM(DATA_LENGTH)SUM(INDEX_LENGTH) FROM information_schema.tables WHERE TABLE_SCHEMAdatabase_name;結果是以…

Fusion組件庫是如何支持多語言能力的

隨著國際化發展&#xff0c;多語言的需求越來越常見&#xff0c;單一的語言已經遠不能滿足需求了。作為一個組件庫&#xff0c;支持多語言也是基本能力。 多語言功能的本質其實是文本的替換&#xff0c;一個詞匯“OK”&#xff0c;在英文語境下是“OK”&#xff0c;日語語境下是…

mysql 存儲過程 replace_mysql replace存儲過程

{"moduleinfo":{"card_count":[{"count_phone":1,"count":1}],"search_count":[{"count_phone":4,"count":4}]},"card":[{"des":"阿里云數據庫專家保駕護航&#xff0c;為用戶…

注解版poi操作工具

*************************************優雅的分割線 ********************************** 分享一波:程序員賺外快-必看的巔峰干貨 如果以上內容對你覺得有用,并想獲取更多的賺錢方式和免費的技術教程 請關注微信公眾號:HB荷包 一個能讓你學習技術和賺錢方法的公眾號,持續更…

Kali Linux 2019.1 發布,Metasploit 更新到 5.0 版本

百度智能云 云生態狂歡季 熱門云產品1折起>>> Kali Linux 2019.1 發布了&#xff0c;Kali 前身 BackTrack&#xff0c;它是一個基于 Debian 的 Linux 發行版&#xff0c;主要用于信息安全行業&#xff0c;其包含了一系列安全、滲透測試和取證工具。此版本 Linux 內核…

peewee mysql_scrapy中利用peewee插入Mysql

前兩天老大布置一個任務&#xff0c;說爬下來的數據要存入數據庫中&#xff0c;丟給我一個peewee&#xff0c;說用這個。當時的我兩眼一抹黑&#xff0c;這是個什么東西呀&#xff0c;我知道scrapy的數據存入數據庫是在pipelines中進行設置但是peewee是什么東西呢。經過兩天不懈…

Java版數據結構與算法——線性表

*************************************優雅的分割線 ********************************** 分享一波:程序員賺外快-必看的巔峰干貨 如果以上內容對你覺得有用,并想獲取更多的賺錢方式和免費的技術教程 請關注微信公眾號:HB荷包 一個能讓你學習技術和賺錢方法的公眾號,持續更…

基于 CODING 的 Spring Boot 持續集成項目

本文作者&#xff1a;CODING 用戶 - 廖石榮 持續集成的概念 持續集成(Continuous integration,簡稱 CI&#xff09;是一種軟件開發實踐&#xff0c;即團隊開發成員經常集成他們的工作&#xff0c;通常每個成員每天至少集成一次&#xff0c;也就意味著每天可能會發生多次集成。每…

lvs mysql 端口_LVS配置及多端口服務配置

一、5、各主機IP地址&#xff1a;主機IP網關Client192.168.86.116RouterF0/0:192.168.x.xFo/1:192.168.xx.xxF0/1DirectorEth0:192.168.86.111/24(DIP)Eth0:1:192.168.86.254/32(VIP)F0/1Real 1Eth0:192.168.86.112/24(DIP)lo:1:192.168.86.254/32(VIP)F0/1Real 2Eth0:192.168.…

Mybatis組成部分

*************************************優雅的分割線 ********************************** 分享一波:程序員賺外快-必看的巔峰干貨 如果以上內容對你覺得有用,并想獲取更多的賺錢方式和免費的技術教程 請關注微信公眾號:HB荷包 一個能讓你學習技術和賺錢方法的公眾號,持續更…

Stream流與Lambda表達式(一) 雜談

一、流 轉換為數組、集合 package com.java.design.java8.Stream;import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner;import java.util.A…

一年java工作經驗-面試總結

*************************************優雅的分割線 ********************************** 分享一波:程序員賺外快-必看的巔峰干貨 如果以上內容對你覺得有用,并想獲取更多的賺錢方式和免費的技術教程 請關注微信公眾號:HB荷包 一個能讓你學習技術和賺錢方法的公眾號,持續更…

linux mysql python包_03_mysql-python模塊, linux環境下python2,python3的

---恢復內容開始---1、Python2 正常[rootIP ~]#pip install mysql-pythonDEPRECATION: Python 2.7 will reach the end of its life on January 1st, 2020. Please upgrade your Python as Python 2.7 wont be maintained after that date. A future version of pip will drop …

我的這套VuePress主題你熟悉吧

最近熬了很多個夜晚, 踩坑無數, 終于寫出了用VuePress驅動的主題. 只需體驗三分鐘&#xff0c;你就會跟我一樣&#xff0c;愛上這款主題. vuepress-theme-indigo-material, 已經發布到npm, 請客官享用~~ 介紹 vuepress-theme-indigo-material 的原主題是hexo-theme-indigo, git…

兩年Java工作經驗應該會些什么技術

*************************************優雅的分割線 ********************************** 分享一波:程序員賺外快-必看的巔峰干貨 如果以上內容對你覺得有用,并想獲取更多的賺錢方式和免費的技術教程 請關注微信公眾號:HB荷包 一個能讓你學習技術和賺錢方法的公眾號,持續更…

centos 6 mysql 5.7.13 編譯安裝_Centos 6.5 下面 源碼編譯 安裝 Mysql 5.7.13

安裝軟件依賴包yum -y install gcc gcc-c ncurses ncurses-devel cmake下載軟件包cd /usr/local/srcwget https://downloads.mysql.com/archives/get/file/mysql-5.7.13.tar.gz --no-check-certificate下載 boost 庫&#xff0c;MySQL 5.7.5 開始Boost庫是必需的cd /usr/loca…

LeetCode 237. 刪除鏈表中的節點(Python3)

題目&#xff1a; 請編寫一個函數&#xff0c;使其可以刪除某個鏈表中給定的&#xff08;非末尾&#xff09;節點&#xff0c;你將只被給定要求被刪除的節點。 現有一個鏈表 -- head [4,5,1,9]&#xff0c;它可以表示為: 示例 1: 輸入: head [4,5,1,9], node 5 輸出: [4,1,9…

使用Uniapp隨手記錄知識點

使用uniapp隨手記錄知識點 1 組件內置組件擴展組件 2 vuex狀態管理使用流程mapState 輔助函數gettersMutation 1 組件 內置組件 內置組件內主要包含一些基礎的view button video scroll-view等內置基礎組件&#xff0c;滿足基礎場景 擴展組件 擴展組件是uniapp封裝了一些成…

一年Java經驗應該會些什么

*************************************優雅的分割線 ********************************** 分享一波:程序員賺外快-必看的巔峰干貨 如果以上內容對你覺得有用,并想獲取更多的賺錢方式和免費的技術教程 請關注微信公眾號:HB荷包 一個能讓你學習技術和賺錢方法的公眾號,持續更…

mysql查詢各類課程的總學分_基于jsp+mysql的JSP學生選課信息管理系統

運行環境: 最好是java jdk 1.8&#xff0c;我們在這個平臺上運行的。其他版本理論上也可以。IDE環境&#xff1a; Eclipse,Myeclipse,IDEA都可以硬件環境&#xff1a; windows 7/8/10 2G內存以上(推薦4G&#xff0c;4G以上更好)可以實現&#xff1a; 學生&#xff0c;教師角色的…