這篇文章的標(biāo)題原本叫做——Java 并發(fā)編程(一):簡(jiǎn)介别渔,作者名叫小二。但我在接到投稿時(shí)覺得這標(biāo)題不夠新穎钾怔,不夠吸引讀者的眼球却桶,就在發(fā)文的時(shí)候強(qiáng)行修改了標(biāo)題(也不咋滴)境输。
小二是一名 Java 程序員,就職于沉默公司颖系,工齡是兩年零一個(gè)月零三天嗅剖。和剛畢業(yè)那會(huì)相比,編程能力已經(jīng)大有提升嘁扼,但領(lǐng)導(dǎo)老王一直沒敢把并發(fā)編程的開發(fā)安排給小二信粮,這讓小二心里耿耿于懷。
這事不怪老王趁啸,小二心里很清楚:編寫正確的程序很難强缘,編寫正確的并發(fā)程序更是難上加難督惰。自己功力還不到那個(gè)份上,萬一搞砸了旅掂,難免讓一向謹(jǐn)慎的老王面上無光赏胚。
小二想來想去,辦法只有一個(gè)商虐,主動(dòng)去學(xué)觉阅!就找老王要了一本《Java并發(fā)編程實(shí)戰(zhàn)》,據(jù)說這本書是并發(fā)編程中的經(jīng)典之作称龙。拿到書后留拾,隨手翻了翻戳晌,竟然發(fā)現(xiàn)里面藏著一封情書:小二激動(dòng)壞了鲫尊,想象著老王寫情話的樣子,不由得笑出來聲沦偎。
(戛然而止)
小二的背景就先介紹到這疫向。接下來,我們來一起鑒賞下小二讀完這本書后寫下的第一篇文章豪嚎。
Java 并發(fā)編程(一):簡(jiǎn)介
01搔驼、為什么需要操作系統(tǒng)
我喜歡在寫文章(不用紙和筆用電腦了)的時(shí)候聽音樂(不用 MP3 用電腦了),假如電腦只能做一件事情的話侈询,我就只能在寫完文章的時(shí)候再聽音樂舌涨,或者聽完音樂的時(shí)候再開始寫作,這樣就很不爽——在沒有操作系統(tǒng)前扔字,的確就是這么不爽囊嘉。
有了操作系統(tǒng)后,情況就變得大不一樣了革为,電腦可以同時(shí)運(yùn)行多個(gè)程序扭粱。通過 TOP
命令可以查看電腦上當(dāng)前正在運(yùn)行的進(jìn)程(和程序有著密切的關(guān)系),見下圖震檩。
通常情況下琢蛤,一個(gè)程序會(huì)至少對(duì)應(yīng)一個(gè)進(jìn)程。上圖中抛虏,“Google Chrome”這三個(gè)進(jìn)程意味著我的電腦上打開著一個(gè)名叫谷歌瀏覽器的程序博其。
讓我們用一段專業(yè)的術(shù)語(yǔ)來描述一下程序和進(jìn)程之間的關(guān)系:
程序是計(jì)算機(jī)為完成特定任務(wù)所執(zhí)行的指令序列。 操作系統(tǒng)允許多道程序并發(fā)執(zhí)行共享系統(tǒng)資源迂猴,而程序在并發(fā)執(zhí)行時(shí)所產(chǎn)生的一系列特點(diǎn)使得傳統(tǒng)的程序概念已經(jīng)不足以對(duì)其進(jìn)行描述慕淡,因此,引入了“進(jìn)程(Process)”:可以更好的描述計(jì)算機(jī)程序的執(zhí)行過程错忱,反映操作系統(tǒng)的并發(fā)執(zhí)行儡率、資源共享及用戶隨機(jī)訪問的特性良蛮,并以此作為資源分配的基本單位。
每當(dāng)一個(gè)程序運(yùn)行時(shí)叠必,操作系統(tǒng)就為該程序創(chuàng)建了一個(gè)進(jìn)程丽焊,并為它分配資源、調(diào)度其運(yùn)行眉孩。程序執(zhí)行結(jié)束后个绍,進(jìn)程也就消亡了。一個(gè)程序被同時(shí)執(zhí)行多次浪汪,系統(tǒng)就會(huì)創(chuàng)建多個(gè)進(jìn)程巴柿。因此,一個(gè)程序可以被多個(gè)進(jìn)程執(zhí)行死遭,一個(gè)進(jìn)程也可以同時(shí)執(zhí)行多個(gè)程序广恢。
當(dāng)然了,對(duì)于現(xiàn)在的操作系統(tǒng)來說呀潭,進(jìn)程并不是最小的調(diào)度單位钉迷,而是線程。線程也被稱為輕量級(jí)進(jìn)程钠署。
由于同一個(gè)進(jìn)程中的所有線程會(huì)共享進(jìn)程的內(nèi)存地址空間糠聪,因此這些線程都能訪問相同的變量,如果沒有明確的同步機(jī)制來協(xié)同對(duì)共享數(shù)據(jù)的訪問谐鼎,那么當(dāng)一個(gè)線程正在使用某個(gè)變量時(shí)舰蟆,另外一個(gè)線程可能同時(shí)訪問這個(gè)變量,就會(huì)造成不可預(yù)測(cè)的結(jié)果狸棍。
02身害、多線程的優(yōu)勢(shì)
查看了一下,我這臺(tái)電腦的物理 CPU(處理器)個(gè)數(shù)只有一個(gè)隔缀,但是核數(shù)(一塊 CPU 上面能處理數(shù)據(jù)的芯片組的數(shù)量)是 4 個(gè)题造。
這意味著,我這臺(tái)電腦能夠在同一時(shí)間處理一個(gè)進(jìn)程內(nèi)的四個(gè)線程任務(wù):線程 A 正在讀取一個(gè)文件猾瘸,線程 B 正在寫入一個(gè)文件界赔,線程 C 正在計(jì)算一個(gè)數(shù)值,線程 D 正在進(jìn)行網(wǎng)絡(luò)傳輸牵触。
我們知道淮悼,進(jìn)行文件讀寫或者網(wǎng)絡(luò)傳輸通常會(huì)發(fā)生阻塞,這也是沒辦法的事揽思。如果沒有多線程的幫助袜腥,程序會(huì)按照順序依次執(zhí)行,也就意味著發(fā)生阻塞的時(shí)候其他任務(wù)只能干巴巴的等著钉汗,什么也做不了羹令。
有了多線程鲤屡,情況就完全不一樣了,線程之間可以互不干擾福侈,從而發(fā)揮處理器的多核能力酒来。
說個(gè)有點(diǎn)讓人難為情的事,我是 Eclipse 的(愚)忠實(shí)用戶肪凛,至今沒切換到 IDEA 陣營(yíng)堰汉。在用 Eclipse 的時(shí)候經(jīng)常會(huì)出現(xiàn)這樣的情況,一個(gè)進(jìn)度被另外一個(gè)卡住伟墙,下一個(gè)必須等待上一個(gè)執(zhí)行完畢才開始執(zhí)行翘鸭。等待的時(shí)候幾乎什么也干不成,點(diǎn)了取消也沒用戳葵!
假如 Eclipse 采用多線程的話就乓,每個(gè)任務(wù)放在單獨(dú)的任務(wù)中執(zhí)行,響應(yīng)就會(huì)快很多譬淳。
03档址、多線程帶來的風(fēng)險(xiǎn)
曾有這樣一則耳熟能詳?shù)墓适隆?/p>
特洛伊人在城外的海灘上發(fā)現(xiàn)了一只巨大的木馬盹兢,他們把它拉進(jìn)了城里而不是把它燒掉或推到海里邻梆,以為這是天神給特洛伊人帶來的賜福。于是绎秒,特洛伊人歡天喜地浦妄,慶祝勝利,他們跳著唱著见芹,喝光了一桶又一桶的酒剂娄,以為希臘人被他們戰(zhàn)敗了。
而故事的結(jié)局大家也都知道了玄呛。希臘人把特洛伊城掠奪成空阅懦,燒成一片灰燼。海倫(宙斯之女徘铝,被稱為“世上最美的女人”耳胎,她和特洛伊王子私奔,引發(fā)了特洛伊戰(zhàn)爭(zhēng))也被墨涅依斯帶回了希臘惕它。
多線程帶來了無與倫比的好處怕午,但也潛藏了巨大的風(fēng)險(xiǎn)(就像那個(gè)木馬)。其中尤為突出的就是安全性問題淹魄。
public class Unsafe {
private int chenmo;
public int add() {
return chenmo++;
}
}
上面這段代碼在單線程的環(huán)境中可以正確執(zhí)行郁惜,但在多線程的環(huán)境中則不能。遞增運(yùn)算 chenmo++
可以拆分為三個(gè)操作:讀取 chenmo甲锡,將 chenmo 加 1兆蕉,將計(jì)算結(jié)果賦值給 chenmo羽戒。兩個(gè)線程可能交替執(zhí)行,發(fā)生下圖中的情況虎韵,于是兩個(gè)線程就會(huì)返回相同的結(jié)果半醉。這也是最常見的一種安全性問題。
其次劝术,多線程還會(huì)引發(fā)活躍性問題:線程 B 需要等待線程 A 釋放它們共有的資源缩多,而線程 A 由于一些問題導(dǎo)致無法釋放資源,那么線程 B 就只能苦苦地等下去养晋。
再者衬吆,多線程還會(huì)引發(fā)性能問題(設(shè)計(jì)良好的多線程當(dāng)然會(huì)提高性能):當(dāng)線程調(diào)度器臨時(shí)掛起一個(gè)活躍中的線程轉(zhuǎn)而運(yùn)行另外一個(gè)線程時(shí),就會(huì)頻繁地出現(xiàn)上下文切換(Context Switch)——開銷很大(掙得多花的也多)绳泉。
04逊抡、單核 CPU 和多核 CPU
來思考一個(gè)問題吧。假如 CPU 只有一個(gè)零酪,核數(shù)也只有一個(gè)冒嫡,多線程還會(huì)有優(yōu)勢(shì)嗎?
閉上眼四苇,讓思維旋轉(zhuǎn)跳躍會(huì)孝凌。
來看答案吧。
單核 CPU 上運(yùn)行的多線程程序月腋,同一時(shí)間只有一個(gè)線程在跑蟀架,系統(tǒng)幫忙進(jìn)行線程切換;系統(tǒng)給每個(gè)線程分配時(shí)間片(大概 10ms)來執(zhí)行榆骚,看起來像是在同時(shí)跑片拍,但實(shí)際上是每個(gè)線程跑一點(diǎn)點(diǎn)就換到其它線程繼續(xù)跑。所以效率不會(huì)有所提高妓肢,線程的切換反到增加了系統(tǒng)開銷捌省。
那多核 CPU 呢?
當(dāng)然有優(yōu)勢(shì)了碉钠!多核需要多線程才能發(fā)揮優(yōu)勢(shì)(不然巧婦難為無米之炊案倩骸),同樣放钦,多線程要在多核上才能有所發(fā)揮(好馬配好鞍吧恰)。
多核 CPU 多線程不僅善于處理 IO 密集型的任務(wù)(減少阻塞時(shí)間)操禀,還善于處理計(jì)算密集型的任務(wù)褂策,比如加密解密、數(shù)據(jù)壓縮解壓縮(視頻、音頻斤寂、普通數(shù)據(jù)等)耿焊,讓每個(gè)核心都物盡其用。
05遍搞、最后
親愛的讀者朋友們罗侯,小二投稿的第一篇文章到此就結(jié)束了。你對(duì)此感到滿意嗎溪猿?或者說你期待下一篇嗎钩杰?
(此時(shí)的小二正在翹首以盼)
上一篇:Java:并發(fā)不易,先學(xué)會(huì)用
下一篇:如何保證共享變量的原子性诊县?
微信搜索「沉默王二」公眾號(hào)讲弄,關(guān)注后回復(fù)「免費(fèi)視頻」獲取 500G 高質(zhì)量教學(xué)視頻(已分門別類)。