java中線程調度遵循的原則_深入理解Java多線程核心知識:跳槽面試必備

多線程相對于其他 Java 知識點來講,有一定的學習門檻,并且了解起來比較費勁。在平時工作中如若使用不當會出現數據錯亂、執行效率低(還不如單線程去運行)或者死鎖程序掛掉等等問題,所以掌握了解多線程至關重要。

本文從基礎概念開始到最后的并發模型由淺入深,講解下線程方面的知識。

概念梳理

本節我將帶大家了解多線程中幾大基礎概念。

并發與并行

并行,表示兩個線程同時做事情。

并發,表示一會做這個事情,一會做另一個事情,存在著調度。單核 CPU 不可能存在并行(微觀上)。1f5c51e3d01a529b34d30624dc038e7f.png

臨界區

臨界區用來表示一種公共資源或者說是共享數據,可以被多個線程使用。但是每一次,只能有一個線程使用它,一旦臨界區資源被占用,其他線程要想使用這個資源,就必須等待。93d3c757fef2582bddc8d29167d6aad3.png

阻塞與非阻塞

阻塞和非阻塞通常用來形容多線程間的相互影響。比如一個線程占用了臨界區資源,那么其它所有需要這個資源的線程就必須在這個臨界區中進行等待,等待會導致線程掛起。這種情況就是阻塞。此時,如果占用資源的線程一直不愿意釋放資源,那么其它所有阻塞在這個臨界區上的線程都不能工作。阻塞是指線程在操作系統層面被掛起。阻塞一般性能不好,需大約8萬個時鐘周期來做調度。非阻塞則允許多個線程同時進入臨界區。

死鎖

死鎖是進程死鎖的簡稱,是指多個進程循環等待他方占有的資源而無限的僵持下去的局面。e34e0b784b4fc98512f9c02ec757f3e8.png

活鎖

假設有兩個線程1、2,它們都需要資源 A/B,假設1號線程占有了 A 資源,2號線程占有了 B 資源;由于兩個線程都需要同時擁有這兩個資源才可以工作,為了避免死鎖,1號線程釋放了 A 資源占有鎖,2號線程釋放了 B 資源占有鎖;此時 AB 空閑,兩個線程又同時搶鎖,再次出現上述情況,此時發生了活鎖。簡單類比,電梯遇到人,一個進的一個出的,對面占路,兩個人同時往一個方向讓路,來回重復,還是堵著路。如果線上應用遇到了活鎖問題,恭喜你中獎了,這類問題比較難排查。

饑餓

饑餓是指某一個或者多個線程因為種種原因無法獲得所需要的資源,導致一直無法執行。

線程的生命周期

在線程的生命周期中,它要經歷創建、可運行、不可運行幾種狀態。

創建狀態

當用 new 操作符創建一個新的線程對象時,該線程處于創建狀態。

處于創建狀態的線程只是一個空的線程對象,系統不為它分配資源。

可運行狀態

執行線程的 start() 方法將為線程分配必須的系統資源,安排其運行,并調用線程體——run()方法,這樣就使得該線程處于可運行狀態(Runnable)。

這一狀態并不是運行中狀態(Running),因為線程也許實際上并未真正運行。

不可運行狀態

當發生下列事件時,處于運行狀態的線程會轉入到不可運行狀態:調用了 sleep() 方法;

線程調用 wait() 方法等待特定條件的滿足;

線程輸入/輸出阻塞;

返回可運行狀態;

處于睡眠狀態的線程在指定的時間過去后;

如果線程在等待某一條件,另一個對象必須通過 notify() 或 notifyAll() 方法通知等待線程條件的改變;

如果線程是因為輸入輸出阻塞,等待輸入輸出完成。

線程的優先級

線程優先級及設置

線程的優先級是為了在多線程環境中便于系統對線程的調度,優先級高的線程將優先執行。一個線程的優先級設置遵從以下原則:線程創建時,子繼承父的優先級;

線程創建后,可通過調用 setPriority() 方法改變優先級;

線程的優先級是1-10之間的正整數。

線程的調度策略

線程調度器選擇優先級最高的線程運行。但是,如果發生以下情況,就會終止線程的運行:線程體中調用了 yield() 方法,讓出了對 CPU 的占用權;

線程體中調用了 sleep() 方法,使線程進入睡眠狀態;

線程由于 I/O 操作而受阻塞;

另一個更高優先級的線程出現;

在支持時間片的系統中,該線程的時間片用完。

單線程創建方式

單線程創建方式比較簡單,一般只有兩種方式:繼承 Thread 類和實現 Runnable 接口;這兩種方式比較常用就不在 Demo 了,但是對于新手需要注意的問題有:不管是繼承 Thread 類還是實現 Runable 接口,業務邏輯是寫在 run 方法里面,線程啟動的時候是執行 start() 方法;

開啟新的線程,不影響主線程的代碼執行順序也不會阻塞主線程的執行;

新的線程和主線程的代碼執行順序是不能夠保證先后的;

對于多線程程序,從微觀上來講某一時刻只有一個線程在工作,多線程目的是讓 CPU 忙起來;

通過查看 Thread 的源碼可以看到,Thread 類是實現了 Runnable 接口的,所以這兩種本質上來講是一個;

PS:平時在工作中也可以借鑒這種代碼結構,對上層調用來講提供更多的選擇,作為服務提供方核心業務歸一維護

為什么要用線程池

通過上面的介紹,完全可以開發一個多線程的程序,為什么還要引入線程池呢。主要是因為上述單線程方式存在以下幾個問題:線程的工作周期:線程創建所需時間為 T1,線程執行任務所需時間為 T2,線程銷毀所需時間為 T3,往往是 T1+T3 大于 T2,所有如果頻繁創建線程會損耗過多額外的時間;

如果有任務來了,再去創建線程的話效率比較低,如果從一個池子中可以直接獲取可用的線程,那效率會有所提高。所以線程池省去了任務過來,要先創建線程再去執行的過程,節省了時間,提升了效率;

線程池可以管理和控制線程,因為線程是稀缺資源,如果無限制的創建,不僅會消耗系統資源,還會降低系統的穩定性,使用線程池可以進行統一的分配,調優和監控;

線程池提供隊列,存放緩沖等待執行的任務。

大致總結了上述的幾個原因,所以可以得出一個結論就是在平時工作中,如果要開發多線程程序,盡量要使用線程池的方式來創建和管理線程。

通過線程池創建線程從調用 API 角度來說分為兩種,一種是原生的線程池,另外該一種是通過 Java 提供的并發包來創建,后者比較簡單,后者其實是對原生的線程池創建方式做了一次簡化包裝,讓調用者使用起來更方便,但道理都是一樣的。所以搞明白原生線程池的原理是非常重要的。

ThreadPoolExecutor

通過 ThreadPoolExecutor 創建線程池。

先來解釋下其中的參數含義(如果看的比較模糊可以大致有個印象,后面的圖是關鍵)。corePoolSize

核心池的大小。

在創建了線程池后,默認情況下,線程池中并沒有任何線程,而是等待有任務到來才創建線程去執行任務,除非調用了 prestartAllCoreThreads() 或者 prestartCoreThread() 方法,從這兩個方法的名字就可以看出,是預創建線程的意思,即在沒有任務到來之前就創建 corePoolSize 個線程或者一個線程。默認情況下,在創建了線程池后,線程池中的線程數為0,當有任務來之后,就會創建一個線程去執行任務,當線程池中的線程數目達到 corePoolSize 后,就會把到達的任務放到緩存隊列當中。maximumPoolSize

線程池最大線程數,這個參數也是一個非常重要的參數,它表示在線程池中最多能創建多少個線程。keepAliveTime

表示線程沒有任務執行時最多保持多久時間會終止。默認情況下,只有當線程池中的線程數大于 corePoolSize 時,keepAliveTime 才會起作用,直到線程池中的線程數不大于 corePoolSize,即當線程池中的線程數大于 corePoolSize 時,如果一個線程空閑的時間達到 keepAliveTime,則會終止,直到線程池中的線程數不超過 corePoolSize。

但是如果調用了 allowCoreThreadTimeOut(boolean) 方法,在線程池中的線程數不大于 corePoolSize 時,keepAliveTime 參數也會起作用,直到線程池中的線程數為0。unit

參數 keepAliveTime 的時間單位。workQueue

一個阻塞隊列,用來存儲等待執行的任務,這個參數的選擇也很重要,會對線程池的運行過程產生重大影響,一般來說,這里的阻塞隊列有以下這幾種選擇:ArrayBlockingQueue、LinkedBlockingQueue、SynchronousQueue。threadFactory

線程工廠,主要用來創建線程。handler

表示當拒絕處理任務時的策略,有以下四種取值:ThreadPoolExecutor.AbortPolicy:丟棄任務并拋出 RejectedExecutionException 異常;

ThreadPoolExecutor.DiscardPolicy:也是丟棄任務,但是不拋出異常;

ThreadPoolExecutor.DiscardOldestPolicy:丟棄隊列最前面的任務,然后重新嘗試執行任務(重復此過程);

ThreadPoolExecutor.CallerRunsPolicy:由調用線程處理該任務。

上面這些參數是如何配合工作的呢?請看下圖:9df1ec4bc10eca4220b9ee536461f592.png

注意圖上面的序號。

簡單總結下線程池之間的參數協作分為以下幾步:線程優先向 CorePool 中提交;

在 Corepool 滿了之后,線程被提交到任務隊列,等待線程池空閑;

在任務隊列滿了之后 corePool 還沒有空閑,那么任務將被提交到 maxPool 中,如果 MaxPool 滿了之后執行 task 拒絕策略。

流程圖如下:9c4acd32b26ecc0ef66fd6a34f68eab7.png

以上就是原生線程池創建的核心原理。除了原生線程池之外并發包還提供了簡單的創建方式,上面也說了它們是對原生線程池的一種包裝,可以讓開發者簡單快捷的創建所需要的線程池。

Executors

newSingleThreadExecutor

創建一個線程的線程池,在這個線程池中始終只有一個線程存在。如果線程池中的線程因為異常問題退出,那么會有一個新的線程來替代它。此線程池保證所有任務的執行順序按照任務的提交順序執行。

newFixedThreadPool

創建固定大小的線程池。每次提交一個任務就創建一個線程,直到線程達到線程池的最大大小。線程池的大小一旦達到最大值就會保持不變,如果某個線程因為執行異常而結束,那么線程池會補充一個新線程。

newCachedThreadPool

可根據實際情況,調整線程數量的線程池,線程池中的線程數量不確定,如果有空閑線程會優先選擇空閑線程,如果沒有空閑線程并且此時有任務提交會創建新的線程。在正常開發中并不推薦這個線程池,因為在極端情況下,會因為 newCachedThreadPool 創建過多線程而耗盡 CPU 和內存資源。

newScheduledThreadPool

此線程池可以指定固定數量的線程來周期性的去執行。比如通過 scheduleAtFixedRate 或者 scheduleWithFixedDelay 來指定周期時間。

PS:另外在寫定時任務時(如果不用 Quartz 框架),最好采用這種線程池來做,因為它可以保證里面始終是存在活的線程的。

推薦使用 ThreadPoolExecutor 方式

在阿里的 Java 開發手冊時有一條是不推薦使用 Executors 去創建,而是推薦去使用 ThreadPoolExecutor 來創建線程池。

這樣做的目的主要原因是:使用 Executors 創建線程池不會傳入核心參數,而是采用的默認值,這樣的話我們往往會忽略掉里面參數的含義,如果業務場景要求比較苛刻的話,存在資源耗盡的風險;另外采用 ThreadPoolExecutor 的方式可以讓我們更加清楚地了解線程池的運行規則,不管是面試還是對技術成長都有莫大的好處。

改了變量,其他線程可以立即知道。保證可見性的方法有以下幾種:volatile

加入 volatile 關鍵字的變量在進行匯編時會多出一個 lock 前綴指令,這個前綴指令相當于一個內存屏障,內存屏障可以保證內存操作的順序。當聲明為 volatile 的變量進行寫操作時,那么這個變量需要將數據寫到主內存中。

由于處理器會實現緩存一致性協議,所以寫到主內存后會導致其他處理器的緩存無效,也就是線程工作內存無效,需要從主內存中重新刷新數據。

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

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

相關文章

java禁止js獲取cookie_java中Cookie被禁用后Session追蹤問題

一.服務器端獲取Session對象依賴于客戶端攜帶的Cookie中的JSESSIONID數據。如果用戶把瀏覽器的隱私級別調到最高,這時瀏覽器是不會接受Cookie、這樣導致永遠在服務器端都拿不到的JSESSIONID信息。這樣就導致服務器端的Session使用不了。Java針對Cookie禁用&#xff…

java類構造方法成員方法練習_面向對象方法論總結 練習(一)

原標題:面向對象方法論總結 & 練習(一)學習目標1.面向對象與面向過程2.類與對象的概念3.類的定義,對象的創建和使用4.封裝5.構造方法6.方法的重載內容1.面向對象與面向過程為什么會出現面向對象反分析方法?因為現實世界太復雜多變&#x…

mysql 統計查詢不充電_MySql查詢語句介紹,單表查詢,來充電吧

mysql在網站開發中,越來越多人使用了,方便部署,方便使用。我們要掌握mysql,首先要學習查詢語句。查詢單個表的數據,和多個表的聯合查詢。下面以一些例子來先簡單介紹下單表查詢。操作方法01首先看下我們例子用到的數據表&#xff…

MySQL線上優化_線上MySQL千萬級大表,如何優化?

前段時間應急群有客服反饋,會員管理功能無法按到店時間、到店次數、消費金額進行排序。經過排查發現是 SQL 執行效率低,并且索引效率低下。圖片來自 Pexels應急問題商戶反饋會員管理功能無法按到店時間、到店次數、消費金額進行排序,一直轉圈…

java底層怎么學_java學習----底層原理一

數據類型java自帶8種基本類型,4中整型類型,2種浮點類型,1種用于表示Unicode編碼的字符單元的字符類型,和1種用于表示真值的bool類型(這8種基本類型都有自己的隱式初始值)。long在申請變量時賦值數據后必須加l或L,若不加…

java實驗報告合肥工業大學_合肥工業大學數據結構上機實驗代碼與實驗報告(全)github地址...

C++實現鏈隊類——合肥工業大學數據結構實驗5:鏈式隊列實驗5 5.1 實驗目的 熟練掌握隊列的順序鏈式存儲結構. 熟練掌握隊列的有關算法設計,并在鏈隊列上實現. 根據具體給定的需求,合理設計并實現相關結構和算法. 5.2 實驗要求 5.2.1鏈隊列實驗要 …

php 類分開寫,自己前幾天寫的無限分類類_PHP教程

自己前幾天寫的無限分類類_PHP教程前一周寫的吧,使用中效果還不錯。主要思想來自:http://www.phpobject.net/b...[urlhttp://www.phpobject.net/blog/read.php?49][/url]這里就不多解釋原理了,直接發代碼。PS:這里代碼是不能直接使用的&…

php創建表設置編碼,教您在Zend Framework里如何設置數據庫編碼以及怎樣給數據表設定前綴!...

當我們在開發項目時..大家都會遇到一個問題就是:數據庫的編碼問題.當然我們不用Zend Framework做為項目開發的框架時..我們可以很快,很容易搞定這個小問題..但是當我們要使用Zend Framewok開發項目時..我們可能一時會不知道如何解決這個小問題..比如我就是這樣的人..在開發這個…

python 怎么將數組轉為列表_怎么將視頻轉為GIF動態圖 表情包怎么制作

說到GIF,大家應該都不陌生了吧!尤其是在聊天中使用較多,似乎一言不合就開啟了斗圖模式,但是我們平時使用的GIF一般都是軟件中自帶的,其實自己制作也是很方便的,而且會發現很有趣,不但可以直接錄…

curl php 百度,php curl 模擬登錄百度主頁

php curl 模擬登錄百度首頁本帖最后由 STONEWP 于 2012-03-29 13:43:04 編輯代碼如下:$url "https://passport.baidu.com/?login&tplmn";//$url "http://www.baidu.com/";$cookdir "d:/www/html/mndl/cookie.txt";$ch curl_…

proteus里面沒有stm32怎么辦_嵌入式單片機之stm32串口你懂了多少!!

stm32作為現在嵌入式物聯網單片機行業中經常要用多的技術,相信大家都有所接觸,今天這篇就給大家詳細的分析下有關于stm32的出口,還不是很清楚的朋友要注意看看了哦,在最后還會為大家分享有些關于stm32的視頻資料便于學習參考。點擊…

tomcat不能解析php,tomcat不支持php怎么辦

tomcat不支持php的解決辦法:首先將“PHP/Java Bridge”下的相關文件復制到tomcat的lib目錄下;然后修改tomcat安裝目錄下conf文件夾里的“web.xml”文件;最后重啟tomcat即可。java開發者都知道,tomcat是用來部署java web項目的。這…

c++ dicom圖像切割_【高訓智造】原創專業課堂第225期--定位滑座的線切割加工

原標題:【高訓智造】原創專業課堂第225期--定位滑座的線切割加工歡迎來到【高訓智造】原創專業課堂第225期,本期由郭沃沛老師給大家帶來線切割小課堂。定位滑座的線切割加工郭沃沛1零件圖如圖1所示為定位滑座零件圖,其材料為45鋼,…

php.ini配置詳解 號,php INI配置文件的解析實現分析

我不知道怎么說才好,因為我在讀INI文件的時候,往往都是用現成的函數:parse_ini_file或者是parse_ini_string,但怎么寫入,就是另外的方法了(自己實現。。。。)所以看到這篇文章的時候,我也才剛剛知道&#x…

c iostream.源碼_通達信指標公式源碼精準買賣主圖指標公式免費分享

V0:EMA(C,5),COLOR00FF66;V1:EMA(C,10),COLOR00FF66;V2:EMA(C,15),LINETHICK2,COLORFFFFFF;V3:EMA(C,30);V4:EMA(C,60),COLOR3366FF;年線:EMA(C,90),COLORBLUE;M1:1000*V1/V4<1015 AND 1000*V1/V4>975;M2:1000*V2/V4<1020 AND 1000*V2/V4>980;M3:1000*V3/V4<101…

4am永遠 鼠標按鍵設置_4AM稱霸PCL和PEL 絕地求生與和平精英的雙端冠軍 | 電玩巴士...

在《絕地求生》PCL秋季賽&#xff0c;4AM高分碾壓全場斬獲冠軍&#xff1b;在手游和平精英PEL聯賽上4AM戰隊再度重拳出擊榮獲S3總冠軍。在同一時間&#xff0c;4AM戰隊實現了端游與手游雙冠王的神跡&#xff01;要說國內第一大逃殺電競俱樂部&#xff0c;4am自認第二&#xff0…

checkA.php,php window平臺模擬checkdnsrr函數檢測_php

在php的系統函數中有一個checkdnsrr函數&#xff0c;該函數的作用是根據一個給定的host name(域名)或者IP地址檢查它是否有DNS記錄&#xff0c;目的就是檢驗它是否真實存在。但是該函數僅可以在linux系統下使用&#xff0c;并不支持windows平臺。下面是網上搜集到的一個hack的方…

oracle臨時表經常被鎖_【趙強老師】Oracle數據庫的存儲結構

Oracle的存儲結構分為&#xff1a;物理存儲結構和邏輯存儲結構。一、物理存儲結構&#xff1a;指硬盤上存在的文件數據文件(data file)一個數據庫可以由多個數據文件組成的&#xff0c;數據文件是真正存放數據庫數據的。一個數據文件就是一個操作系統文件。數據庫的對象(表和索…

php頁面的循環輸出數組,PHP抓取頁面上的數組 并循環輸出 急

PHP抓取頁面上的數組 并循環輸出 急 在線等我用file_get_contents()抓取了 這個網址上的內容http://simonfenci.sinaapp.com/index.php?keysimon&wd1314abc看似好像反回的是數組。。但是我不管怎么用foreach循環都報錯。。我只想把數組中的word里面的值 取出來。。誰幫幫我…

h5 nan_手把手教你將H5游戲打包成快游戲

H5游戲可以通過快應用的web組件快速打包成快游戲&#xff0c;打包上架后的快游戲&#xff0c;只要原H5游戲的url不發生變動&#xff0c;快游戲就不需要做更新&#xff0c;維護工作量小。使用快應用IDE&#xff0c;打包快游戲的操作很簡單。訪問官網安裝開發工具&#xff0c;在P…