基礎(chǔ)知識
1最域、CPU核心數(shù)和線程數(shù)的關(guān)系
- 多核心:也指單芯片多處理器( Chip Multiprocessors,簡稱CMP),CMP是由美國斯坦福大學(xué)提出的,其思想是將大規(guī)模并行處理器中的SMP(對稱多處理器)集成到同一芯片內(nèi),各個(gè)處理器并行執(zhí)行不同的進(jìn)程相味。這種依靠多個(gè)CPU同時(shí)并行地運(yùn)行程序是實(shí)現(xiàn)超高速計(jì)算的一個(gè)重要方向,稱為并行處理
- 多線程: Simultaneous Multithreading.簡稱SMT.SMT可通過復(fù)制處理器上的結(jié)構(gòu)狀態(tài),讓同一個(gè)處理器上的多個(gè)線程同步執(zhí)行并共享處理器的執(zhí)行資源可最大限度地實(shí)現(xiàn)寬發(fā)射昭抒、亂序的超標(biāo)量處理,提高處理器運(yùn)算部件的利用率,緩和由于數(shù)據(jù)相關(guān)或 Cache未命中帶來的訪問內(nèi)存延時(shí)歪赢。
核心數(shù)品抽、線程數(shù):目前主流CPU有雙核遍搞、三核和四核,六核也在2010年發(fā)布疗认。增加核心數(shù)目就是為了增加線程數(shù),因?yàn)椴僮飨到y(tǒng)是通過線程來執(zhí)行任務(wù)的,一般情況下它們是1:1對應(yīng)關(guān)系,也就是說四核CPU一般擁有四個(gè)線程较锡。但 Intel引入超線程技術(shù)后,使核心數(shù)與線程數(shù)形成1:2的關(guān)系业稼, -
自己的電腦如下
image.png
2、CPU時(shí)間片輪轉(zhuǎn)機(jī)制
時(shí)間片輪轉(zhuǎn)調(diào)度是一種最古老蚂蕴、最簡單盼忌、最公平且使用最廣的算法,又稱RR調(diào)度。每個(gè)進(jìn)程被分配一個(gè)時(shí)間段,稱作它的時(shí)間片,即該進(jìn)程允許運(yùn)行的時(shí)間掂墓。
如果在時(shí)間片結(jié)束時(shí)進(jìn)程還在運(yùn)行,則CPU將被剝奪并分配給另一個(gè)進(jìn)程谦纱。如果進(jìn)程在時(shí)間片結(jié)束前阻塞或結(jié)來,則CPU當(dāng)即進(jìn)行切換。調(diào)度程序所要做的就是維護(hù)一張就緒進(jìn)程列表,當(dāng)進(jìn)程用完它的時(shí)間片后,它被移到隊(duì)列的末尾
時(shí)間片輪轉(zhuǎn)調(diào)度中唯一有趣的一點(diǎn)是時(shí)間片的長度君编。從一個(gè)進(jìn)程切換到另一個(gè)進(jìn)程是需要定時(shí)間的,包括保存和裝入寄存器值及內(nèi)存映像,更新各種表格和隊(duì)列等跨嘉。假如進(jìn)程切( processwitch),有時(shí)稱為上下文切換( context switch),需要5ms,再假設(shè)時(shí)間片設(shè)為20ms,則在做完20ms有用的工作之后,CPU將花費(fèi)5ms來進(jìn)行進(jìn)程切換。CPU時(shí)間的20%被浪費(fèi)在了管理開銷上了吃嘿。
為了提高CPU效率,我們可以將時(shí)間片設(shè)為5000ms祠乃。這時(shí)浪費(fèi)的時(shí)間只有0.1%。但考慮到在一個(gè)分時(shí)系統(tǒng)中,如果有10個(gè)交互用戶幾乎同時(shí)按下回車鍵,將發(fā)生什么情況?假設(shè)所有其他進(jìn)程都用足它們的時(shí)間片的話,最后一個(gè)不幸的進(jìn)程不得不等待5s才獲得運(yùn)行機(jī)會兑燥。多數(shù)用戶無法忍受一條簡短命令要5才能做出響應(yīng),同樣的問題在一臺支持多道程序的個(gè)人計(jì)算機(jī)上也會發(fā)
結(jié)論可以歸結(jié)如下:時(shí)間片設(shè)得太短會導(dǎo)致過多的進(jìn)程切換,降低了CPU效率:而設(shè)得太長又可能引起對短的交互請求的響應(yīng)變差亮瓷。將時(shí)間片設(shè)為100ms通常是一個(gè)比較合理的折衷。3降瞳、什么是進(jìn)程和線程
進(jìn)程是程序運(yùn)行資源分配的最小單位
進(jìn)程是操作系統(tǒng)進(jìn)行資源分配的最小單位,其中資源包括:CPU嘱支、內(nèi)存空間蚓胸、磁盤等,同一進(jìn)程中的多條線程共享該進(jìn)程中的全部系統(tǒng)資源,而進(jìn)程和進(jìn)程之間是相互獨(dú)立的。進(jìn)程是具有一定獨(dú)立功能的程序關(guān)于某個(gè)數(shù)據(jù)集合上的一次運(yùn)行活動,進(jìn)程是系統(tǒng)進(jìn)行資源分配和調(diào)度的一個(gè)獨(dú)立單位除师。
進(jìn)程是程序在計(jì)算機(jī)上的一次執(zhí)行活動沛膳。當(dāng)你運(yùn)行一個(gè)程序,你就啟動了一個(gè)進(jìn)程。顯然,程序是死的汛聚、靜態(tài)的,進(jìn)程是活的锹安、動態(tài)的。進(jìn)程可以分為系統(tǒng)進(jìn)程和用戶進(jìn)程倚舀。凡是用于完成操作系統(tǒng)的各種功能的進(jìn)程就是系統(tǒng)進(jìn)程,它們就是處于運(yùn)行狀態(tài)下的操作系統(tǒng)本身,用戶進(jìn)程就是所有由你啟動的進(jìn)程叹哭。線程是cpu調(diào)度的最小單位,必須依賴于進(jìn)程而存在
線程是進(jìn)程的一個(gè)實(shí)體,是CPU調(diào)度和分派的基本單位,它是比進(jìn)程更小的、能獨(dú)立運(yùn)行的基本單位痕貌。線程自己基本上不擁有系統(tǒng)資源,只擁有一點(diǎn)在運(yùn)行中必不可少的資源(如程序計(jì)數(shù)器,一組寄存器和棧),但是它可與同屬一個(gè)進(jìn)程的其他的線程共享進(jìn)程所擁有的全部資源风罩。
線程無處不在
任何一個(gè)程序都必須要?jiǎng)?chuàng)建線程,特別是Java不管任何程序都必須啟動一個(gè)main函數(shù)的主線程; Java Web開發(fā)里面的定時(shí)任務(wù)、定時(shí)器芯侥、JSP和 Servlet、異步消息處理機(jī)制,遠(yuǎn)程訪問接口RM等,任何一個(gè)監(jiān)聽事件, onclick的觸發(fā)事件等都離不開線程和并發(fā)的知識乳讥。4柱查、并行和并發(fā)
如果有條高速公路A上面并排有8條車道,那么最大的并行車輛就是8輛此條高速公路A同時(shí)并排行走的車輛小于等于8輛的時(shí)候,車輛就可以并行運(yùn)行。CPU也是這個(gè)原理,一個(gè)CPU相當(dāng)于一個(gè)高速公路A,核心數(shù)或者線程數(shù)就相當(dāng)于并排可以通行的車道;而多個(gè)CPU就相當(dāng)于并排有多條高速公路,而每個(gè)高速公路并排有多個(gè)車道云石。
當(dāng)談?wù)摬l(fā)的時(shí)候一定要加個(gè)單位時(shí)間,也就是說單位時(shí)間內(nèi)并發(fā)量是多少?離開了單位時(shí)間其實(shí)是沒有意義的唉工。
綜合來說:
并發(fā):指應(yīng)用能夠交替執(zhí)行不同的任務(wù),比如單CPU核心下執(zhí)行多線程并非是同時(shí)執(zhí)行多個(gè)任務(wù),如果你開兩個(gè)線程執(zhí)行,就是在你幾乎不可能察覺到的速度不斷去切換這兩個(gè)任務(wù),已達(dá)到"同時(shí)執(zhí)行效果",其實(shí)并不是的,只是計(jì)算機(jī)的速度太快,我們無法察覺到而已.
并行:指應(yīng)用能夠同時(shí)執(zhí)行不同的任務(wù),例:吃飯的時(shí)候可以邊吃飯邊打電話,這兩件事情可以同時(shí)執(zhí)行兩者區(qū)別:一個(gè)是交替執(zhí)行,一個(gè)是同時(shí)執(zhí)行.
5、高并發(fā)編程的意義汹忠、好處和注意事項(xiàng)
充分利用CPU的資源
加快響應(yīng)用戶的時(shí)間
可以使你的代碼模塊化,異步化,簡單化
安卓的ui線程
多線程程序需要注意事項(xiàng)
(1)線程之間的安全性
在同一個(gè)進(jìn)程里面的多線程是資源共享的,也就是都可以訪問同一個(gè)內(nèi)存地址當(dāng)中的一個(gè)變量淋硝。例如:若每個(gè)線程中對全局變量、靜態(tài)變量只有讀操作,而無寫操作,一般來說,這個(gè)全局變量是線程安全的:若有多個(gè)線程同時(shí)執(zhí)行寫操作,一般都需要考慮線程同步,否則就可能影響線程安全(2)線程之間的死循環(huán)過程:這個(gè)特別嚴(yán)重宽菜,死鎖
為了解決線程之間的安全性引入了Java的鎖機(jī)制,而一不小心就會產(chǎn)生Java線程死鎖的多線程問題,因?yàn)椴煌木€程都在等待那些根本不可能被釋放的鎖,從而導(dǎo)致所有的工作都無法完成(3)線程太多了會將服務(wù)器資源耗盡形成死機(jī)當(dāng)機(jī)
Java里的程序天生就是多線程的
一個(gè)Java程序從main()方法開始執(zhí)行谣膳,然后按照既定的代碼邏輯執(zhí)行,看似沒有其他線程參與铅乡,但實(shí)際上Java程序天生就是多線程程序继谚,因?yàn)閳?zhí)行main()方法的是一個(gè)名稱為main的線程。
- [6] Monitor Ctrl-Break //監(jiān)控Ctrl-Break中斷信號的
- [5] Attach Listener //內(nèi)存dump阵幸,線程dump花履,類信息統(tǒng)計(jì),獲取系統(tǒng)屬性等
- [4] Signal Dispatcher // 分發(fā)處理發(fā)送給JVM信號的線程
- [3] Finalizer // 調(diào)用對象finalize方法的線程
- [2] Reference Handler//清除Reference的線程
- [1] main //main線程挚赊,用戶程序入
線程的啟動與中止
啟動
啟動線程的方式有:
- 1诡壁、X extends Thread;,然后X.run
- 2荠割、X implements Runnable妹卿;然后交給Thread運(yùn)行
- 3、X implements Callable;然后交給Thread運(yùn)行
第1纽帖、2方式都有一個(gè)缺陷就是:在執(zhí)行完任務(wù)之后無法獲取執(zhí)行結(jié)果宠漩。從Java 1.5開始,就提供了Callable和Future懊直,通過它們可以在任務(wù)執(zhí)行完畢之后得到任務(wù)執(zhí)行結(jié)果扒吁。
但是其實(shí)只有兩種,因?yàn)榈谌N的方式看著像是實(shí)現(xiàn)了Callable的接口室囊,但是呢雕崩,其實(shí)到底還是實(shí)現(xiàn)了Runnable的接口,一個(gè)類實(shí)現(xiàn)了Callable融撞,然后new 出對象盼铁,對象交給FutureTask,然后通過線程start
1尝偎、看FutureTask這個(gè)類饶火,是實(shí)現(xiàn)了RunnableFuture接口
2、RunnableFuture繼承了致扯,Runnable的接口和Future接口肤寝,所以到底還是Runnable的之類,所以就只有兩種
Callable抖僵、Future和FutureTask
Runnable是一個(gè)接口鲤看,在它里面只聲明了一個(gè)run()方法,由于run()方法返回值為void類型耍群,所以在執(zhí)行完任務(wù)之后無法返回任何結(jié)果义桂。
Callable位于java.util.concurrent包下,它也是一個(gè)接口蹈垢,在它里面也只聲明了一個(gè)方法慷吊,只不過這個(gè)方法叫做call(),這是一個(gè)泛型接口曹抬,call()函數(shù)返回的類型就是傳遞進(jìn)來的V類型罢浇。
Future就是對于具體的Runnable或者Callable任務(wù)的執(zhí)行結(jié)果進(jìn)行取消、查詢是否完成沐祷、獲取結(jié)果嚷闭。必要時(shí)可以通過get方法獲取執(zhí)行結(jié)果,該方法會阻塞直到任務(wù)返回結(jié)果赖临。
因?yàn)镕uture只是一個(gè)接口胞锰,所以是無法直接用來創(chuàng)建對象使用的,因此就有了下面的FutureTask兢榨。
FutureTask類實(shí)現(xiàn)了RunnableFuture接口嗅榕,RunnableFuture繼承了Runnable接口和Future接口顺饮,而FutureTask實(shí)現(xiàn)了RunnableFuture接口。所以它既可以作為Runnable被線程執(zhí)行凌那,又可以作為Future得到Callable的返回值兼雄。
事實(shí)上,F(xiàn)utureTask是Future接口的一個(gè)唯一實(shí)現(xiàn)類帽蝶。
要new一個(gè)FutureTask的實(shí)例赦肋,有兩種方法
中止
線程自然終止:要么是run執(zhí)行完成了,要么是拋出了一個(gè)未處理的異常導(dǎo)致線程提前結(jié)束励稳。
手動中止
暫停佃乘、恢復(fù)和停止操作對應(yīng)在線程Thread的API就是suspend()、resume()和stop()驹尼。但是這些API是過期的趣避,也就是不建議使用的。不建議使用的原因主要有:以suspend()方法為例新翎,在調(diào)用后程帕,線程不會釋放已經(jīng)占有的資源(比如鎖),而是占有著資源進(jìn)入睡眠狀態(tài)地啰,這樣容易引發(fā)死鎖問題愁拭。同樣,stop()方法在終結(jié)一個(gè)線程時(shí)不會保證線程的資源正常釋放髓绽,通常是沒有給予線程完成資源釋放工作的機(jī)會敛苇,因此會導(dǎo)致程序可能工作在不確定狀態(tài)下妆绞。正因?yàn)閟uspend()、resume()和stop()方法帶來的副作用,這些方法才被標(biāo)注為不建議使用的過期方法禽笑。
安全的中止則是其他線程通過調(diào)用某個(gè)線程A的interrupt()方法對其進(jìn)行中斷操作, 中斷好比其他線程對該線程打了個(gè)招呼恕洲,“A,你要中斷了”图焰,不代表線程A會立即停止自己的工作启盛,同樣的A線程完全可以不理會這種中斷請求。因?yàn)閖ava里的線程是協(xié)作式的技羔,不是搶占式的僵闯。線程通過檢查自身的中斷標(biāo)志位是否被置為true來進(jìn)行響應(yīng),線程通過方法isInterrupted()來進(jìn)行判斷是否被中斷藤滥,也可以調(diào)用靜態(tài)方法Thread.interrupted()來進(jìn)行判斷當(dāng)前線程是否被中斷鳖粟,不過Thread.interrupted()會同時(shí)將中斷標(biāo)識位改寫為false。
如果一個(gè)線程處于了阻塞狀態(tài)(如線程調(diào)用了thread.sleep拙绊、thread.join向图、thread.wait泳秀、),則在線程在檢查中斷標(biāo)示時(shí)如果發(fā)現(xiàn)中斷標(biāo)示為true榄攀,則會在這些阻塞方法調(diào)用處拋出InterruptedException異常嗜傅,并且在拋出異常后會立即將線程的中斷標(biāo)示位清除,即重新設(shè)置為false檩赢。
不建議自定義一個(gè)取消標(biāo)志位來中止線程的運(yùn)行吕嘀。因?yàn)閞un方法里有阻塞調(diào)用時(shí)會無法很快檢測到取消標(biāo)志,線程必須從阻塞調(diào)用返回后漠畜,才會檢查這個(gè)取消標(biāo)志币他。這種情況下,使用中斷會更好憔狞,因?yàn)楹ぃ弧⒁话愕淖枞椒遥鐂leep等本身就支持中斷的檢查拍冠,二、檢查中斷位的狀態(tài)和檢查取消標(biāo)志位沒什么區(qū)別簇抵,用中斷位的狀態(tài)還可以避免聲明取消標(biāo)志位庆杜,減少資源的消耗。
注意****:處于****死鎖狀態(tài)****的****線程無法被中斷
幾個(gè)方法
- start()方法讓一個(gè)線程進(jìn)入就緒隊(duì)列等待分配cpu碟摆,分到cpu后才調(diào)用實(shí)現(xiàn)的run()方法晃财,start()方法不能重復(fù)調(diào)用。
- run方法是業(yè)務(wù)邏輯實(shí)現(xiàn)的地方典蜕,本質(zhì)上和任意一個(gè)類的任意一個(gè)成員方法并沒有任何區(qū)別断盛,可以重復(fù)執(zhí)行,可以被單獨(dú)調(diào)用愉舔。
- yield()方法:使當(dāng)前線程讓出CPU占有權(quán)钢猛,但讓出的時(shí)間是不可設(shè)定的。也不會釋放鎖資源轩缤,所有執(zhí)行yield()的線程有可能在進(jìn)入到可執(zhí)行狀態(tài)后馬上又被執(zhí)行命迈。
- join方法:把指定的線程加入到當(dāng)前線程,可以將兩個(gè)交替執(zhí)行的線程合并為順序執(zhí)行的線程火的。比如在線程B中調(diào)用了線程A的Join()方法壶愤,直到線程A執(zhí)行完畢后,才會繼續(xù)執(zhí)行線程B馏鹤。
一張圖
一個(gè)線程新建征椒,到start()-就緒了,如果獲取join()執(zhí)行權(quán)就會運(yùn)行假瞬,時(shí)間片到期 yield就會從運(yùn)行狀態(tài)轉(zhuǎn)化到就緒
運(yùn)行的狀態(tài)陕靠,可以通過sleep() 到阻塞狀態(tài)迂尝,同時(shí)run()到結(jié)束,也是就是一個(gè)死亡的狀態(tài)剪芥。Stop不建議去使用垄开,因?yàn)檫@樣子會導(dǎo)致線程有很多的碎片化的空間,線程中的任務(wù)沒有去執(zhí)行完成税肪,也可以通過wait 去到阻塞的情況
線程阻塞可以通過 notify notifyAll去喚醒他溉躲,同時(shí)sleep時(shí)間到也可以到就緒的狀態(tài),如果一個(gè)線程處于了阻塞狀態(tài)(如線程調(diào)用了thread.sleep益兄、thread.join锻梳、thread.wait、)净捅,則在線程在檢查中斷標(biāo)示時(shí)如果發(fā)現(xiàn)中斷標(biāo)示為true疑枯,則會在這些阻塞方法調(diào)用處拋出InterruptedException異常,并且在拋出異常后會立即將線程的中
關(guān)于 yield的方法蛔六,在 ConcurrentHashMap 類中的initTable方法中就調(diào)用了
關(guān)于Doug Lea 荆永,李大爺,這句話可以這么說:編程不識DougLea国章,寫盡java也枉然
道格拉斯·李(Douglas S. Lea)是紐約州立大學(xué)奧斯威戈分校的計(jì)算機(jī)科學(xué)教授和現(xiàn)任計(jì)算機(jī)科學(xué)系主任具钥,他專門研究并發(fā)編程和并發(fā)數(shù)據(jù)結(jié)構(gòu)的設(shè)計(jì)。他是Java Community Process執(zhí)行委員會的主席液兽,并擔(dān)任JSR 166主席骂删,該委員會將并發(fā)實(shí)用程序添加到Java編程語言中(請參閱Java concurrency)。2010年10月22日四啰,Doug Lea通知Java社區(qū)流程執(zhí)行委員會宁玫,他不贊成連任。 Lea再次當(dāng)選為2012 OpenJDK理事會的一般會員
-
Doug Lea寫的java.util.concurrent備受推崇拟逮。AQS的思想奠基者
image.png -
HashMap也是李大爺?shù)慕艹鲋?/p>
image.png