只有光頭才能變強。文本已收錄至個人GitHub精選文章,歡迎Star:https://github.com/ZhongFuCheng3y/3yjava
在上週總結了一篇「工做中經常使用到的Java集合類」,反響還不錯。這周來寫寫Java另外一個重要的知識點:「多線程」git
多線程你們在初學的時候,對這個知識點應該有很多的疑惑的。我認爲主要緣由有兩個:github
這就會給人帶來一種感受「這破玩意涉及的東西是真的廣,平時也不怎麼用,怎麼面試就恰恰愛問這個鬼東西」面試
很少BB,我要開始了。數據庫
首先,咱們要明確的是「爲何要使用多線程」,可能有人會認爲「使用多線程就是爲了加快程序運行的速度啊」。若是你是這樣回答了,那面試官可能會問你「那多線程是怎麼加快程序運行速度的?」後端
於個人理解:使用多線程最主要的緣由是提升系統的資源利用率。安全
如今CPU基本都是多核的,若是你只用單線程,那就是隻用到了一個核心,其餘的核心就至關於空閒在那裏了。微信
廁所的坑位有5個,若是隻用一個坑位,那不是很虧?好比如今我有5我的要上廁所。在單線程的時候:進去一我的解決要10分鐘,而後後面的人都得等一個坑位。那總的時間就要花費50分鐘。多線程
在多線程的時候,進去一我的要解決10分鐘,而後後面的人發現還有別的坑位,就去別的坑位了,不是傻瓜地等一個坑位。框架
咱們能夠把「等坑位」看做是IO操做,衆所周知IO操做相對於CPU而言是很是慢的,CPU等待IO那段時間是空閒的。若是咱們須要作相似IO這種慢的操做,能夠開多個線程出來,儘可能不要讓CPU空閒下來,提升系統的資源利用率。
說白了,咱們就是在「壓榨」CPU的資源。原本就有的資源,若是有須要,咱們就應當好好利用。
多線程不是銀彈,並非說線程越多,咱們的資源利用效率就越好。執行IO操做咱們線程能夠適當多一點,由於不少時候CPU是相對空閒的。若是是計算型的操做,原本CPU就不空閒了,還開不少的線程就不對了(有多線程就會有線程切換的問題,線程切換都是須要耗費資源的)
多線程其實離咱們很近,只是不少時候咱們感知不到它的存在而已。
Tomcat我相信每一個Java後端的同窗都認識它,它就是以多線程去響應請求的,咱們能夠在server.xml
中配置鏈接池的配置,好比:
<Connector port="8080" maxThreads="350" maxHttpHeaderSize="8192" minSpareThreads="45" maxPostSize="512000" protocol="HTTP/1.1" enableLookups="false" redirectPort="8443" acceptCount="200" keepAliveTimeout="15000" maxKeepAliveRequests="-1" maxConnections="25000" connectionTimeout="15000" disableUploadTimeout="false" useBodyEncodingForURI="true" URIEncoding="UTF-8" />
Tomcat處理每個請求都會從線程鏈接池裏邊用一個線程去處理,這顯然是多線程的操做。而後這個請求線程順藤摸瓜到了咱們的Servlet,執行對應的service()
方法。
而咱們的service
方法是無狀態的,多個線程請求service
方法,每每都沒有操做共享變量,不操做共享變量就不會有線程安全問題。
上面只是用了Servlet舉例,咱們經常使用的SpringMVC其實也是同樣的(畢竟底層仍是Servlet)。
還有咱們在鏈接數據庫的時候,也會用對應的鏈接池(Druid、C3P0、DBCP等),好比常見的Druid配置:
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"> <property name="url" value="${jdbc_url}" /> <property name="username" value="${jdbc_user}" /> <property name="password" value="${jdbc_password}" /> <property name="filters" value="stat" /> <property name="maxActive" value="20" /> <property name="initialSize" value="1" /> <property name="maxWait" value="60000" /> <property name="minIdle" value="1" /> <property name="timeBetweenEvictionRunsMillis" value="60000" /> <property name="minEvictableIdleTimeMillis" value="300000" /> <property name="testWhileIdle" value="true" /> <property name="testOnBorrow" value="false" /> <property name="testOnReturn" value="false" /> <property name="poolPreparedStatements" value="true" /> <property name="maxOpenPreparedStatements" value="20" /> <property name="asyncInit" value="true" /> </bean>
我想說的是:咱們平常開發的程序幾乎都是多線程模式的,只是絕大多數時候咱們沒感知到而已。不少時候都是框架幫咱們屏蔽掉了。
從上面總結下來,咱們能夠發現:咱們平常「關於多線程的代碼」寫得很少,可是咱們寫的程序代碼的的確確是在多線程的環境下跑的。
若是咱們不懂多線程知識,很直接的一個現實:
從文章最開頭的思惟導圖,咱們能夠發現多線程的知識點仍是不少的,咱們起碼得知道:
雖然在工做中未必會所有用得上,但若是項目真的用到了,咱們若是學過了可能就能夠很快地理解當時爲何要這樣設計(我以爲去挖掘過程仍是挺有意思的)。
「我可能不用,但你必需要有」
這個道理也很容易懂:「我買電腦的時候,雖然我是木耳聽不出什麼音質出來,但你音質就是得好」。企業招人的時候也同樣「你在工做的時候未必要寫,但你必需要會」
至少在我看來,從求職的角度觸發,多線程是很重要的。以前我還整理過在我當時校招常常被問到的多線程面試題目:
原本是打算這篇文章主旋律就寫這塊的,而後我翻了一下本身維護的系統,用到的線程的地方還真的不是不少...
我就拿我如今的系統用到線程相關知識的幾個例子吧。
我這邊有個調度系統,運營設置了對應的時間,該任務就去執行,執行的內容大體就是去讀HDFS文件,而後將數據組裝,再傳遞到下游。
任務觸發了之後,咱們直接將這個任務交給一個線程池去處理,交由線程池後就直接返回SUCCESS
。
這樣作的好處是什麼?若是多個任務同時觸發,那可能某些任務執行時間過長,請求可能會被阻塞住,而咱們若是放在線程池中能夠提升系統的吞吐量。
使用線程池的時候,每每咱們的調用方都不須要考慮請求是否立馬處理成功。假設線程池在處理任務的時候由於某些緣由失敗了,咱們能夠走報警機制(用郵件/短信等渠道去提醒請求方便可)。
不知道你們學過消息隊列了沒有,咱們經常說消息隊列是異步的,不少時候調用方的請求咱們丟到消息隊列裏邊,就告訴調用方咱們這條請求處理成功了。實際上,這個請求可能還交由下游的多個系統去處理,下游的系統可能也是異步的.....
在使用線程池的時候,不少時候咱們也是把他當作異步來使(WebFlux實際上也是將請求丟到線程池嘛),只要咱們的系統之間交互不是強一致性的,又但願提升系統的吞吐量,咱們就能夠考慮使用線程池。
有的時候,咱們須要有一個線程去輪詢處理某些任務。
好比,個人系統會有發短信的功能,我調用渠道商的下發接口的後,我須要拿到短信的回執信息,因而我就須要去調用渠道商的回執接口。
此時最簡單的作法就是開一個線程,不斷的輪詢渠道商的回執接口(咱們設定輪詢的間隔時間便可)
Thread thread = new Thread(new Runnable() { @Override public void run() { while (true) { try { // 間隔一段時間輪詢一次 TimeUnit.MILLISECONDS.sleep(period); // 調用接口 String result = http.post(); // 獲得result後進行處理(好比將結果插入到數據庫) smsDao.insert(result); } } }); thread.start();
或者有的時候,咱們把任務放到內存阻塞隊列或者Redis,也是經過一個線程輪詢去取「隊列」的數據。
juc其實就是java.util.concurrent
包
咱們在使用線程的時候,或者在平常開發的時候,都是得考慮咱們如今使用的場景是不是線程安全的。
若是不是線程安全的,咱們能夠作什麼東西來使咱們的程序變得線程安全。
如今已經工做有一段時間了,爲何還來寫多線程
呢,緣由有如下幾個:
read.me
會常常更換。如今的GitHub導航也不合我心意了(太長了),而且早期的文章,說實話排版也不太行,我決定從新搞一波。基於上面的緣由,我決定把個人系列文章彙總成一個PDF/HTML/WORD
文檔。說實話,打造這麼一個文檔花了我很多的時間。爲了防止白嫖,關注個人公衆號回覆「888」便可獲取。
PDF的內容很是很是長,乾貨很是很是的硬,有興趣的同窗能夠瀏覽一波。共有「129」頁
文檔的內容均爲手打,有任何的不懂均可以直接來問我(公衆號有個人聯繫方式)。
若是你們想要實時關注我更新的文章以及分享的乾貨的話,微信搜索Java3y。