線程、多線程藤违、線程池詳解

線程

進(jìn)程就是在某種程度上相互隔離浪腐、獨(dú)立運(yùn)行的程序。和進(jìn)程一樣顿乒,線程在程序中是獨(dú)立的议街、并發(fā)的執(zhí)行路徑,每個線程有它自己的堆棧璧榄、自己的程序計(jì)數(shù)器和自己的局部變量特漩。線程就是在進(jìn)程中負(fù)責(zé)程序執(zhí)行的執(zhí)行單元吧雹,一個進(jìn)程中至少有一個線程。

進(jìn)程可以支持多個線程涂身,它們看似同時執(zhí)行吮炕,但互相之間并不同步。一個進(jìn)程中的多個線程共享相同的內(nèi)存地址空間访得,這就意味著它們可以訪問相同的變量和對象龙亲,而且它們從同一堆中分配對象。

多線程

解決多個任務(wù)同時執(zhí)行的需求悍抑,合理利用CPU的資源鳄炉。

多線程的運(yùn)行是根據(jù)CPU的切換來完成的,如何切換由CPU來完成搜骡,因此多線程運(yùn)行具有不確定性拂盯。

線程池

在未引入線程池之前,我們使用線程時就去創(chuàng)建一個線程记靡,這樣實(shí)現(xiàn)起來非常方便谈竿,但是就會有一個問題:

如果并發(fā)的線程數(shù)量很多,并且每個線程都是執(zhí)行一個時間很短的任務(wù)就結(jié)束了摸吠,這樣頻繁創(chuàng)建線程就會大大降低系統(tǒng)的效率空凸,因?yàn)轭l繁創(chuàng)建線程和銷毀線程需要時間。那么有沒有一種辦法使得線程可以復(fù)用寸痢,就是執(zhí)行完一個任務(wù)呀洲,并不被銷毀,而是可以繼續(xù)執(zhí)行其他的任務(wù)啼止?

在Java中可以通過線程池來達(dá)到這樣的效果道逗。

基本思想還是一種對象池的思想,開辟一塊內(nèi)存空間献烦,里面存放了眾多(未死亡)的線程滓窍,池中線程執(zhí)行調(diào)度由池管理器來處理。當(dāng)有線程任務(wù)時巩那,從池中取一個吏夯,執(zhí)行完成后線程對象歸池,這樣可以避免反復(fù)創(chuàng)建線程對象所帶來的性能開銷拢操,節(jié)省了系統(tǒng)的資源锦亦。

1.創(chuàng)建線程的兩種方式

①繼承自Thread類,重寫run()方法


繼承Thread類令境,覆蓋run()方法杠园。

創(chuàng)建線程對象并用start()方法啟動線程。


面試題

1)線程和進(jìn)程有什么區(qū)別舔庶?

一個進(jìn)程是一個獨(dú)立(self contained)的運(yùn)行環(huán)境抛蚁,它可以被看作一個程序或者一個應(yīng)用陈醒。而線程是在進(jìn)程中執(zhí)行的一個任務(wù)。線程是進(jìn)程的子集瞧甩,一個進(jìn)程可以有很多線程钉跷,每條線程并行執(zhí)行不同的任務(wù)。不同的進(jìn)程使用不同的內(nèi)存空間肚逸,而所有的線程共享一片相同的內(nèi)存空間爷辙。別把它和棧內(nèi)存搞混,每個線程都擁有單獨(dú)的棧內(nèi)存用來存儲本地?cái)?shù)據(jù)朦促。

2)Thread 類中的 start() 和 run() 方法有什么區(qū)別膝晾?

調(diào)用 start() 方法才會啟動新線程;如果直接調(diào)用 Thread 的 run() 方法务冕,它的行為就會和普通的方法一樣血当;為了在新的線程中執(zhí)行我們的代碼,必須使用 Thread.start() 方法禀忆。

②實(shí)現(xiàn)Runnable接口臊旭,重寫run()方法

面試題

1)用 Runnable 還是 Thread ?

我們都知道可以通過繼承 Thread 類或者調(diào)用 Runnable 接口來實(shí)現(xiàn)線程箩退,問題是創(chuàng)建線程哪種方式更好呢离熏?什么情況下使用它?這個問題很容易回答乏德,如果你知道Java不支持類的多重繼承撤奸,但允許你調(diào)用多個接口。所以如果你要繼承其他類喊括,當(dāng)然是調(diào)用Runnable接口更好了。

2)Runnable 和 Callable 有什么不同矢棚?

Runnable 和 Callable 都代表那些要在不同的線程中執(zhí)行的任務(wù)郑什。Runnable 從 JDK1.0 開始就有了,Callable 是在 JDK1.5 增加的蒲肋。它們的主要區(qū)別是 Callable 的 call() 方法可以返回值和拋出異常蘑拯,而 Runnable 的 run() 方法沒有這些功能。Callable 可以返回裝載有計(jì)算結(jié)果的 Future 對象兜粘。

Callable接口類似于Runnable申窘,兩者都是為那些其實(shí)例可能被另一個線程執(zhí)行的類設(shè)計(jì)的。但是Runnable不會返回結(jié)果孔轴,并且無法拋出經(jīng)過檢查的異常剃法。

2.多線程

多線程的概念很好理解就是多條線程同時存在,但要用好多線程確不容易路鹰,涉及到多線程間通信贷洲,多線程共用一個資源等諸多問題收厨。

使用多線程的優(yōu)缺點(diǎn):

優(yōu)點(diǎn):

1)適當(dāng)?shù)奶岣叱绦虻膱?zhí)行效率(多個線程同時執(zhí)行)。

2)適當(dāng)?shù)奶岣吡速Y源利用率(CPU优构、內(nèi)存等)诵叁。

缺點(diǎn):

1)占用一定的內(nèi)存空間。

2)線程越多CPU的調(diào)度開銷越大钦椭。

3)程序的復(fù)雜度會上升拧额。

synchronized

同步塊大家都比較熟悉,通過 synchronized 關(guān)鍵字來實(shí)現(xiàn)彪腔;所有加上 synchronized 的方法和塊語句势腮,在多線程訪問的時候,同一時刻只能有一個線程能夠訪問漫仆。

wait()捎拯、notify()贴见、notifyAll()

這三個方法是 java.lang.Object 的 final native 方法招拙,任何繼承 java.lang.Object 的類都有這三個方法。它們是Java語言提供的實(shí)現(xiàn)線程間阻塞和控制進(jìn)程內(nèi)調(diào)度的底層機(jī)制酗钞,平時我們會很少用到的吗浩。

wait():

導(dǎo)致線程進(jìn)入等待狀態(tài)建芙,直到它被其他線程通過notify()或者notifyAll喚醒,該方法只能在同步方法中調(diào)用懂扼。

notify():

隨機(jī)選擇一個在該對象上調(diào)用wait方法的線程禁荸,解除其阻塞狀態(tài),該方法只能在同步方法或同步塊內(nèi)部調(diào)用阀湿。

notifyAll():

解除所有那些在該對象上調(diào)用wait方法的線程的阻塞狀態(tài)赶熟,同樣該方法只能在同步方法或同步塊內(nèi)部調(diào)用。

調(diào)用這三個方法中任意一個陷嘴,當(dāng)前線程必須是鎖的持有者映砖,如果不是會拋出一個 IllegalMonitorStateException 異常。

wait() 與 Thread.sleep(long time) 的區(qū)別

sleep():在指定的毫秒數(shù)內(nèi)讓當(dāng)前正在執(zhí)行的線程休眠(暫停執(zhí)行)灾挨,該線程不丟失任何監(jiān)視器的所屬權(quán)邑退,sleep() 是 Thread 類專屬的靜態(tài)方法,針對一個特定的線程劳澄。

wait() 方法使實(shí)體所處線程暫停執(zhí)行地技,從而使對象進(jìn)入等待狀態(tài),直到被 notify() 方法通知或者 wait() 的等待的時間到秒拔。sleep() 方法使持有的線程暫停運(yùn)行莫矗,從而使線程進(jìn)入休眠狀態(tài),直到用 interrupt 方法來打斷他的休眠或者 sleep 的休眠的時間到。

wait() 方法進(jìn)入等待狀態(tài)時會釋放同步鎖趣苏,而 sleep() 方法不會釋放同步鎖狡相。所以,當(dāng)一個線程無限 sleep 時又沒有任何人去 interrupt 它的時候食磕,程序就產(chǎn)生大麻煩了尽棕,notify() 是用來通知線程,但在 notify() 之前線程是需要獲得 lock 的彬伦。另個意思就是必須寫在 synchronized(lockobj) {...} 之中滔悉。wait() 也是這個樣子,一個線程需要釋放某個 lock单绑,也是在其獲得 lock 情況下才能夠釋放回官,所以 wait() 也需要放在 synchronized(lockobj) {...} 之中。

volatile 關(guān)鍵字

volatile 是一個特殊的修飾符搂橙,只有成員變量才能使用它歉提。在Java并發(fā)程序缺少同步類的情況下,多線程對成員變量的操作對其它線程是透明的区转。volatile 變量可以保證下一個讀取操作會在前一個寫操作之后發(fā)生苔巨。線程都會直接從內(nèi)存中讀取該變量并且不緩存它。這就確保了線程讀取到的變量是同內(nèi)存中是一致的废离。

ThreadLocal 變量

ThreadLocal 是Java里一種特殊的變量侄泽。每個線程都有一個 ThreadLocal 就是每個線程都擁有了自己獨(dú)立的一個變量,競爭條件被徹底消除了蜻韭。如果為每個線程提供一個自己獨(dú)有的變量拷貝悼尾,將大大提高效率。首先肖方,通過復(fù)用減少了代價高昂的對象的創(chuàng)建個數(shù)闺魏。其次,你在沒有使用高代價的同步或者不變性的情況下獲得了線程安全窥妇。

join() 方法

join() 方法定義在 Thread 類中舷胜,所以調(diào)用者必須是一個線程,join() 方法主要是讓調(diào)用該方法的 Thread 完成 run() 方法里面的東西后活翩,再執(zhí)行 join() 方法后面的代碼。

Thread.yield() 方法

Thread.sleep(long time):線程暫時終止執(zhí)行(睡眠)一定的時間翻伺。

Thread.yield():線程放棄運(yùn)行材泄,將CPU的控制權(quán)讓出。

這兩個方法都會將當(dāng)前運(yùn)行線程的CPU控制權(quán)讓出來吨岭,但 sleep() 方法在指定的睡眠時間內(nèi)一定不會再得到運(yùn)行機(jī)會拉宗,直到它的睡眠時間完成;而 yield() 方法讓出控制權(quán)后,還有可能馬上被系統(tǒng)的調(diào)度機(jī)制選中來運(yùn)行旦事,比如魁巩,執(zhí)行yield()方法的線程優(yōu)先級高于其他的線程,那么這個線程即使執(zhí)行了 yield() 方法也可能不能起到讓出CPU控制權(quán)的效果姐浮,因?yàn)樗尦隹刂茩?quán)后谷遂,進(jìn)入排隊(duì)隊(duì)列,調(diào)度機(jī)制將從等待運(yùn)行的線程隊(duì)列中選出一個等級最高的線程來運(yùn)行卖鲤,那么它又(很可能)被選中來運(yùn)行肾扰。

3.線程池

頂級接口? :? Executor

子接口? ? :? ? ExecutorService

實(shí)現(xiàn)類? ? :? ? ThreadPoolExecutor

創(chuàng)建一個線程池:


普通類Executors 提供四種線程池:

1)newCachedThreadPool 是一個可根據(jù)需要創(chuàng)建新線程的線程池,但是在以前構(gòu)造的線程可用時將重用它們蛋逾。對于執(zhí)行很多短期異步任務(wù)的程序而言集晚,這些線程池通常可提高程序性能区匣。調(diào)用 execute() 將重用以前構(gòu)造的線程(如果線程可用)偷拔。如果現(xiàn)有線程沒有可用的,則創(chuàng)建一個新線程并添加到池中亏钩。終止并從緩存中移除那些已有 60 秒鐘未被使用的線程莲绰。因此,長時間保持空閑的線程池不會使用任何資源铸屉。注意钉蒲,可以使用 ThreadPoolExecutor 構(gòu)造方法創(chuàng)建具有類似屬性但細(xì)節(jié)不同(例如超時參數(shù))的線程池。

2)newSingleThreadExecutor 創(chuàng)建是一個單線程池彻坛,也就是該線程池只有一個線程在工作顷啼,所有的任務(wù)是串行執(zhí)行的,如果這個唯一的線程因?yàn)楫惓=Y(jié)束昌屉,那么會有一個新的線程來替代它钙蒙,此線程池保證所有任務(wù)的執(zhí)行順序按照任務(wù)的提交順序執(zhí)行。

3)newFixedThreadPool 創(chuàng)建固定大小的線程池间驮,每次提交一個任務(wù)就創(chuàng)建一個線程躬厌,直到線程達(dá)到線程池的最大大小,線程池的大小一旦達(dá)到最大值就會保持不變竞帽,如果某個線程因?yàn)閳?zhí)行異常而結(jié)束扛施,那么線程池會補(bǔ)充一個新線程。

4)newScheduledThreadPool 創(chuàng)建一個大小無限的線程池屹篓,此線程池支持定時以及周期性執(zhí)行任務(wù)的需求疙渣。

通過實(shí)現(xiàn)類 ThreadPoolExecutor 的構(gòu)造函數(shù),了解線程池相關(guān)參數(shù)的概念:


1)corePoolSize:線程池的核心線程數(shù)堆巧,一般情況下不管有沒有任務(wù)都會一直在線程池中一直存活妄荔,只有在 ThreadPoolExecutor 中的方法 allowCoreThreadTimeOut(boolean value) 設(shè)置為 true 時泼菌,閑置的核心線程會存在超時機(jī)制,如果在指定時間沒有新任務(wù)來時啦租,核心線程也會被終止哗伯,而這個時間間隔由第3個屬性 keepAliveTime 指定。

2)maximumPoolSize:線程池所能容納的最大線程數(shù)篷角,當(dāng)活動的線程數(shù)達(dá)到這個值后焊刹,后續(xù)的新任務(wù)將會被阻塞。

3)keepAliveTime:控制線程閑置時的超時時長内地,超過則終止該線程伴澄。一般情況下用于非核心線程,只有在 ThreadPoolExecutor 中的方法 allowCoreThreadTimeOut(boolean value) 設(shè)置為 true時阱缓,也作用于核心線程非凌。

4)unit:用于指定 keepAliveTime 參數(shù)的時間單位,TimeUnit 是個 enum 枚舉類型荆针,常用的有:TimeUnit.HOURS(小時)敞嗡、TimeUnit.MINUTES(分鐘)、TimeUnit.SECONDS(秒) 和 TimeUnit.MILLISECONDS(毫秒)等航背。

5)workQueue:線程池的任務(wù)隊(duì)列喉悴,通過線程池的 execute(Runnable command) 方法會將任務(wù) Runnable 存儲在隊(duì)列中。

6)threadFactory:線程工廠玖媚,它是一個接口箕肃,用來為線程池創(chuàng)建新線程的。

線程池的關(guān)閉

ThreadPoolExecutor 提供了兩個方法今魔,用于線程池的關(guān)閉勺像,分別是 shutdown() 和 shutdownNow()。

shutdown():不會立即的終止線程池错森,而是要等所有任務(wù)緩存隊(duì)列中的任務(wù)都執(zhí)行完后才終止吟宦,但再也不會接受新的任務(wù)。

shutdownNow():立即終止線程池涩维,并嘗試打斷正在執(zhí)行的任務(wù)殃姓,并且清空任務(wù)緩存隊(duì)列,返回尚未執(zhí)行的任務(wù)瓦阐。

面試題

1)什么是 Executor 框架蜗侈?

Executor框架在Java 5中被引入,Executor 框架是一個根據(jù)一組執(zhí)行策略調(diào)用睡蟋、調(diào)度宛篇、執(zhí)行和控制的異步任務(wù)的框架。

無限制的創(chuàng)建線程會引起應(yīng)用程序內(nèi)存溢出薄湿,所以創(chuàng)建一個線程池是個更好的的解決方案,因?yàn)榭梢韵拗凭€程的數(shù)量并且可以回收再利用這些線程。利用 Executor 框架可以非常方便的創(chuàng)建一個線程池豺瘤。

2)Executors 類是什么吆倦?

Executors為Executor、ExecutorService坐求、ScheduledExecutorService蚕泽、ThreadFactory 和 Callable 類提供了一些工具方法。Executors 可以用于方便的創(chuàng)建線程池桥嗤。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末须妻,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子泛领,更是在濱河造成了極大的恐慌荒吏,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件渊鞋,死亡現(xiàn)場離奇詭異绰更,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)锡宋,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進(jìn)店門儡湾,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人执俩,你說我怎么就攤上這事徐钠。” “怎么了役首?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵尝丐,是天一觀的道長。 經(jīng)常有香客問我宋税,道長摊崭,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任杰赛,我火速辦了婚禮呢簸,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘乏屯。我一直安慰自己根时,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布辰晕。 她就那樣靜靜地躺著蛤迎,像睡著了一般。 火紅的嫁衣襯著肌膚如雪含友。 梳的紋絲不亂的頭發(fā)上替裆,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天校辩,我揣著相機(jī)與錄音,去河邊找鬼辆童。 笑死宜咒,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的把鉴。 我是一名探鬼主播故黑,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼庭砍!你這毒婦竟也來了场晶?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤怠缸,失蹤者是張志新(化名)和其女友劉穎诗轻,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體凯旭,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡概耻,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了罐呼。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片鞠柄。...
    茶點(diǎn)故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖嫉柴,靈堂內(nèi)的尸體忽然破棺而出厌杜,到底是詐尸還是另有隱情,我是刑警寧澤计螺,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布夯尽,位于F島的核電站,受9級特大地震影響登馒,放射性物質(zhì)發(fā)生泄漏匙握。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一陈轿、第九天 我趴在偏房一處隱蔽的房頂上張望圈纺。 院中可真熱鬧,春花似錦麦射、人聲如沸蛾娶。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蛔琅。三九已至,卻和暖如春峻呛,著一層夾襖步出監(jiān)牢的瞬間罗售,已是汗流浹背辜窑。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留莽囤,地道東北人谬擦。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像朽缎,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子谜悟,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評論 2 354

推薦閱讀更多精彩內(nèi)容

  • 為加深對線程的理解,首先推薦一篇文章:我是一個線程 線程 進(jìn)程就是在某種程度上相互隔離蔚叨、獨(dú)立運(yùn)行的程序床蜘。和進(jìn)程一樣...
    itcode閱讀 1,393評論 0 12
  • ??一個任務(wù)通常就是一個程序,每個運(yùn)行中的程序就是一個進(jìn)程蔑水。當(dāng)一個程序運(yùn)行時邢锯,內(nèi)部可能包含了多個順序執(zhí)行流,每個順...
    OmaiMoon閱讀 1,671評論 0 12
  • 先看幾個概念:線程:進(jìn)程中負(fù)責(zé)程序執(zhí)行的執(zhí)行單元。一個進(jìn)程中至少有一個線程歇父。多線程:解決多任務(wù)同時執(zhí)行的需求蒂培,合理...
    yeying12321閱讀 541評論 0 0
  • 第一部分 來看一下線程池的框架圖护戳,如下: 1、Executor任務(wù)提交接口與Executors工具類 Execut...
    壓抑的內(nèi)心閱讀 4,263評論 1 24
  • 先看幾個概念:線程:進(jìn)程中負(fù)責(zé)程序執(zhí)行的執(zhí)行單元。一個進(jìn)程中至少有一個線程羔飞。 多線程:解決多任務(wù)同時執(zhí)行的需求肺樟,合...
    孫福生微博閱讀 95,585評論 38 314