日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

java中线程调度遵循的原则_深入理解Java多线程核心知识:跳槽面试必备

發(fā)布時間:2025/3/8 java 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java中线程调度遵循的原则_深入理解Java多线程核心知识:跳槽面试必备 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

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

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

概念梳理

本節(jié)我將帶大家了解多線程中幾大基礎(chǔ)概念。

并發(fā)與并行

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

并發(fā),表示一會做這個事情,一會做另一個事情,存在著調(diào)度。單核 CPU 不可能存在并行(微觀上)。

臨界區(qū)

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

阻塞與非阻塞

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

死鎖

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

活鎖

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

饑餓

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

線程的生命周期

在線程的生命周期中,它要經(jīng)歷創(chuàng)建、可運行、不可運行幾種狀態(tài)。

創(chuàng)建狀態(tài)

當用 new 操作符創(chuàng)建一個新的線程對象時,該線程處于創(chuàng)建狀態(tài)。

處于創(chuàng)建狀態(tài)的線程只是一個空的線程對象,系統(tǒng)不為它分配資源。

可運行狀態(tài)

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

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

不可運行狀態(tài)

當發(fā)生下列事件時,處于運行狀態(tài)的線程會轉(zhuǎn)入到不可運行狀態(tài):調(diào)用了 sleep() 方法;

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

線程輸入/輸出阻塞;

返回可運行狀態(tài);

處于睡眠狀態(tài)的線程在指定的時間過去后;

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

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

線程的優(yōu)先級

線程優(yōu)先級及設(shè)置

線程的優(yōu)先級是為了在多線程環(huán)境中便于系統(tǒng)對線程的調(diào)度,優(yōu)先級高的線程將優(yōu)先執(zhí)行。一個線程的優(yōu)先級設(shè)置遵從以下原則:線程創(chuàng)建時,子繼承父的優(yōu)先級;

線程創(chuàng)建后,可通過調(diào)用 setPriority() 方法改變優(yōu)先級;

線程的優(yōu)先級是1-10之間的正整數(shù)。

線程的調(diào)度策略

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

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

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

另一個更高優(yōu)先級的線程出現(xiàn);

在支持時間片的系統(tǒng)中,該線程的時間片用完。

單線程創(chuàng)建方式

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

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

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

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

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

PS:平時在工作中也可以借鑒這種代碼結(jié)構(gòu),對上層調(diào)用來講提供更多的選擇,作為服務(wù)提供方核心業(yè)務(wù)歸一維護

為什么要用線程池

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

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

線程池可以管理和控制線程,因為線程是稀缺資源,如果無限制的創(chuàng)建,不僅會消耗系統(tǒng)資源,還會降低系統(tǒng)的穩(wěn)定性,使用線程池可以進行統(tǒng)一的分配,調(diào)優(yōu)和監(jiān)控;

線程池提供隊列,存放緩沖等待執(zhí)行的任務(wù)。

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

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

ThreadPoolExecutor

通過 ThreadPoolExecutor 創(chuàng)建線程池。

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

核心池的大小。

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

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

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

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

參數(shù) keepAliveTime 的時間單位。workQueue

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

線程工廠,主要用來創(chuàng)建線程。handler

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

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

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

ThreadPoolExecutor.CallerRunsPolicy:由調(diào)用線程處理該任務(wù)。

上面這些參數(shù)是如何配合工作的呢?請看下圖:

注意圖上面的序號。

簡單總結(jié)下線程池之間的參數(shù)協(xié)作分為以下幾步:線程優(yōu)先向 CorePool 中提交;

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

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

流程圖如下:

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

Executors

newSingleThreadExecutor

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

newFixedThreadPool

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

newCachedThreadPool

可根據(jù)實際情況,調(diào)整線程數(shù)量的線程池,線程池中的線程數(shù)量不確定,如果有空閑線程會優(yōu)先選擇空閑線程,如果沒有空閑線程并且此時有任務(wù)提交會創(chuàng)建新的線程。在正常開發(fā)中并不推薦這個線程池,因為在極端情況下,會因為 newCachedThreadPool 創(chuàng)建過多線程而耗盡 CPU 和內(nèi)存資源。

newScheduledThreadPool

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

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

推薦使用 ThreadPoolExecutor 方式

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

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

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

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

由于處理器會實現(xiàn)緩存一致性協(xié)議,所以寫到主內(nèi)存后會導(dǎo)致其他處理器的緩存無效,也就是線程工作內(nèi)存無效,需要從主內(nèi)存中重新刷新數(shù)據(jù)。

總結(jié)

以上是生活随笔為你收集整理的java中线程调度遵循的原则_深入理解Java多线程核心知识:跳槽面试必备的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 成人av网站在线观看 | 日韩欧美在线一区二区 | 国产视频二区三区 | 国产又粗又猛又爽又黄视频 | 欧美一道本 | 麻豆av一区二区三区久久 | 尤物视频在线观看免费 | 免费人成在线观看网站 | 99国产精品一区 | 三年大片在线观看 | 深夜啪啪 | www.欧美com| 亚洲国产精品成人va在线观看 | 伊人成人久久 | 成人欧美一区二区三区黑人免费 | 亚洲成av人在线观看 | 黄色在线视频播放 | 男人和女人日b视频 | 四虎久久久 | 国产欧美精品一区二区在线播放 | 国产草草草 | 337p亚洲欧洲色噜噜噜 | 精品国产伦一区二区三区免费 | 日本久久久久久久久久久 | 97久久久久 | 亚洲天堂日本 | 亚洲欧美精品一区二区三区 | 丰满熟女人妻一区二区三区 | 貂蝉被到爽流白浆在线观看 | 美丽姑娘免费观看在线观看 | 久久精品国产亚洲av蜜臀色欲 | 丝袜国产一区 | 免费在线观看小视频 | 中文字幕免费高清在线观看 | 麻豆福利在线 | 91超碰在线 | 91视频最新地址 | 国产精品久久久久蜜臀 | 欧美精品一区二区三区在线 | 天天干天天干天天 | 亚洲欧美一区二区三区孕妇 | 日本一区二区三区视频在线观看 | 99婷婷| 亚洲黄色在线网站 | 色涩网站 | 久久久精品网站 | 九九久久99 | 亚洲国产成人精品久久 | 免费在线 | ass亚洲熟妇毛耸耸pics | 久久人体视频 | 丰满少妇一区 | 探花国产精品一区二区 | 亚洲欧美乱综合图片区小说区 | 91免费看. | 精品久久精品久久 | 国产精品永久免费视频 | 色屁屁www影院免费观看入口 | 国产免费久久精品国产传媒 | 少妇av在线 | 亚洲精品一区二区三区婷婷月 | 一级黄色大毛片 | a级性生活视频 | 1024视频在线 | 少妇扒开粉嫩小泬视频 | 午夜一区二区三区 | 久久噜| 激情五月婷婷综合 | 另类视频一区 | 青青99| 欧美性受xxxx黑人xyx | 亚洲精品888 | 欧美精品videos另类日本 | 69视频在线看 | 午夜精彩视频 | 国产精品福利在线播放 | 色妹子综合 | 日韩国产在线播放 | 国产成人av一区 | 国产精品无码内射 | 国产伦精品 | 久久成人久久爱 | 成年人在线网站 | 国产视频一区在线观看 | 国产精品夜夜躁视频 | 一区二区蜜桃 | 黄色大片一级 | 成人午夜性视频 | 久久久久久电影 | 免费观看视频在线观看 | 樱花av在线 | 中国丰满老太hd | 视频福利一区 | 永久免费不卡在线观看黄网站 | 高清国产一区二区 | 自拍偷拍第1页 | 国产丝袜在线 | 日韩欧美国产中文字幕 | 亚洲精品免费在线观看视频 |