您好,登錄后才能下訂單哦!
我記得最開始接觸多進(jìn)程,多線程這一塊的時(shí)候我不是怎么理解,為什么要有多線程?。慷嗑€程到底是個(gè)什么鬼???我一個(gè)程序好好的就可以運(yùn)行為什么要用到多線程???反正我是十分費(fèi)解,即使過了很長時(shí)間我還是不是很懂,聽別人說過也自己試過,但總是沒有理解透徹;
時(shí)間過了很久感覺現(xiàn)在對多線程有了一點(diǎn)新的理解,我們還是從最基本的開始,順便看看從jvm的角度看看多線程在jvm中是怎么分配內(nèi)存的,順便和前面的幾篇內(nèi)容串一下;
1.現(xiàn)實(shí)中的多線程
舉個(gè)例子:假如你一個(gè)人在家,你現(xiàn)在聽首歌5分鐘,燒開水需要10分鐘,玩一局游戲要20分鐘,現(xiàn)在問題來了,你完成這三件事總共需要多少分鐘?
假如是小學(xué)生肯定會回答5+10+20=35分鐘啊,但我們比小學(xué)生牛一點(diǎn),稍微思考一下就知道是20分鐘,因?yàn)槿驴梢酝瑫r(shí)做嘛,玩游戲的同時(shí)可以聽歌,順便燒開水,一把游戲打完,歌聽完了,水也燒開然后可以去泡茶了,舒服!
我們用一個(gè)比較簡陋的圖看看這兩種方式(這里先不考慮并發(fā)與并行的區(qū)別,方便理解)
可以粗略的看到如果是小學(xué)生的話,要一件事一件事的做,最后花的時(shí)間是三者時(shí)間總和;而我們比較聰明,由于三件事互不影響,我們可以三件事同時(shí)開始做,這樣就大大減少了不必要的等待時(shí)間,最終三者花費(fèi)的時(shí)間差不多就是最長的那一個(gè)。
這里稍微提一下并發(fā)和并行的區(qū)別;
并發(fā):這個(gè)是在計(jì)算機(jī)單核CPU的前提之下,我們要清楚一個(gè)CPU在某一時(shí)刻只能做一件事,但是現(xiàn)在有三件事(聽歌,燒開水,玩游戲)交給CPU做,CPU是個(gè)好人,任勞任怨,一下子去聽歌,一下子去燒開水,一下子玩游戲,最終可以把三件事都給做完,但是假如同時(shí)有幾百件事交給CPU做呢?emmmm,最后CPU就被累垮了,住院去了,于是我們計(jì)算機(jī)也卡死了;舉個(gè)最貼近我們的例子:以前上學(xué)的時(shí)候作業(yè)太多,很多時(shí)候都是很多科目的作業(yè)都沒有做完,那怎么辦呢?只有早上去早點(diǎn)去抄一下同學(xué)的,但是各個(gè)課代表來收作業(yè)了,于是只能這個(gè)科目作業(yè)抄一點(diǎn)馬上又把另外一個(gè)科目作業(yè)抄一點(diǎn),瑪?shù)?,最后終于在規(guī)定時(shí)間都抄完了,可是假如你有100個(gè)科目的作業(yè)沒做完,你會怎么辦?用命去抄也抄不完了,于是你就累病了。。。。
并行:多核CPU的前提,現(xiàn)在一個(gè)電腦都有多個(gè)CPU,那么CPU同時(shí)就可以做多件事,即使事情再多,多個(gè)CPU進(jìn)行切換最終花費(fèi)的時(shí)間確實(shí)大大減少;還是說說上面抄作業(yè)的例子,假如你現(xiàn)在有10門科目的作業(yè)沒做完,就靠你一個(gè)頭腦一只手肯定來不及??!于是這個(gè)時(shí)候你喚醒了前世的記憶,原來你是哪吒轉(zhuǎn)世,特么的居然可以變成是三頭六臂,這得可以同時(shí)抄多少份作業(yè)啊?。?!一下子作業(yè)就做完了,舒服!但是這個(gè)時(shí)候作業(yè)科目太多的話你即使有三頭六臂也不夠用啊,而且相互之間的協(xié)調(diào)也就變成一個(gè)很重要的問題。
并發(fā)和并行就是這個(gè)意思,我們現(xiàn)在只關(guān)注并發(fā),看看在單核CPU的計(jì)算機(jī)中一個(gè)程序是怎么運(yùn)行的?
2.進(jìn)程和線程
想想什么叫做進(jìn)程呢?我的理解就是程序進(jìn)入了內(nèi)存就是進(jìn)程,比如我們電腦桌面雙擊QQ,優(yōu)酷,java虛擬機(jī)等,操作系統(tǒng)就會把這些軟件的內(nèi)容加載到內(nèi)存中去運(yùn)行去了,然后就是運(yùn)行某編程語言寫的代碼,轉(zhuǎn)化為機(jī)器碼調(diào)用操作系統(tǒng)的接口,然后操作系統(tǒng)的內(nèi)核會那些硬件驅(qū)動(dòng)程序發(fā)出一些指令,然后我們的電腦屏幕就出現(xiàn)變化了。。。我們簡單畫一畫圖,我們主要看JVM
我們再進(jìn)入JVM中看看,其中線程1、2、3就是我們在java代碼中要去實(shí)現(xiàn)的;
進(jìn)程:我們百度一下進(jìn)程的定義,最重要的一點(diǎn)就是進(jìn)程是操作系統(tǒng)資源分配的基本單位,因?yàn)槊繂?dòng)一個(gè)程序,一個(gè)進(jìn)程就創(chuàng)建了,在操作系統(tǒng)堆內(nèi)存空間上就開辟了一塊空間,也就是分配了資源。
線程:現(xiàn)在再來看線程,百度一下線程定義,其實(shí)就是說:進(jìn)程就是一個(gè)程序,這個(gè)程序之中可能會同時(shí)執(zhí)行多個(gè)任務(wù)的代碼,每一個(gè)任務(wù)就是一個(gè)線程,而且每一個(gè)線程都會在JVM中有自己獨(dú)立的java棧,pc寄存器,而且CPU只能切換線程,即使是不同程序的線程也可以相互切換。
這里就要說明一下,想比進(jìn)程和線程,創(chuàng)建一個(gè)進(jìn)程是要在操作系統(tǒng)內(nèi)存中去開辟空間,會涉及到對操作系統(tǒng)一些函數(shù)的調(diào)用,而創(chuàng)建一個(gè)線程(比如在JVM中)只需要在jvm中個(gè)部分開辟空間,相比較之下,肯定是創(chuàng)建線程所耗費(fèi)的操作系統(tǒng)資源比較少,但是也不可能無限制的創(chuàng)建很多線程,不然jvm也會出問題!
我隨便查了一下,一般的web服務(wù)器線程數(shù)最大不能超過CPU核數(shù)*50,如:8核 < 300,16核 < 800,根據(jù)實(shí)際情況還可以適當(dāng)調(diào)一下。
記得有句話叫做多個(gè)線程之間會競爭CPU資源這句話當(dāng)初我可是很久都沒有理解,這競爭CPU資源到底什么鬼?CPU的資源到底是什么???emmmm..
記得以前家里比較窮,沒有像現(xiàn)在一樣手機(jī)電腦這么多,家里只有一個(gè)電視!但是有的時(shí)候家里人每個(gè)人喜歡看的節(jié)目都不一樣,于是不可避免的相互之間就為了爭這個(gè)遙控器而發(fā)生沖突,哈哈哈!這個(gè)時(shí)候遙控器就相當(dāng)于CPU,我們每個(gè)人都相當(dāng)于一個(gè)線程要完成自己的事情。但是遙控器就一個(gè),就會相互搶遙控器,有的時(shí)候我搶過來遙控器看火影忍者沒到一分鐘,就被我姐搶去看美食節(jié)目,沒過一會兒遙控器就被我爸搶去看新聞去了。。。。。
3.java中的多線程用法
java之中用多線程主要是3種方式:類,接口,線程池,接下來我們就隨意看一下這三種方式
3.1.類
這種方式主要是繼承Thread類,實(shí)現(xiàn)run()方法,run()方法就是我們所需要做的任務(wù)的邏輯代碼,然后將這個(gè)類實(shí)例化調(diào)用start()方法,表示現(xiàn)在這個(gè)線程隨時(shí)可以被CPU調(diào)用;
我還是以上面玩游戲,燒開水和聽歌為例,隨意寫個(gè)小例子:
注意:這里先不看GC,前臺線程有四個(gè)線程,我們創(chuàng)建的三個(gè),還有執(zhí)行main方法的這個(gè)線程(這個(gè)也叫主線程),我們只能保證主線程最優(yōu)先運(yùn)行,至于這四個(gè)線程哪個(gè)先停止,隨機(jī)。。。
3.2.接口
這種方式也差不多,實(shí)現(xiàn)Runnable接口,實(shí)現(xiàn)其中的run()方法,然后實(shí)例化這個(gè)對象并傳入Thread類中,再調(diào)用start()方法;
3.3.線程池
什么是線程池呢?你看看我們上面寫的創(chuàng)建線程的方法,都是用的時(shí)候就去創(chuàng)建,用完了就銷毀,下次又要用就又去創(chuàng)建,這種做法很不好,因?yàn)槊看蝿?chuàng)建和銷毀線程都是很消耗jvm內(nèi)部資源的,因?yàn)樵趈vm內(nèi)部會進(jìn)行申請空間,分配空間和釋放空間各種操作,對jvm的性能會有一定的影響,而且假如某個(gè)特殊的情況下每個(gè)線程只會運(yùn)行很短的時(shí)間就會結(jié)束,那么就會十分頻繁的常見和銷毀線程,導(dǎo)致在jvm中頻繁的申請和釋放內(nèi)存,這極大的影響jvm的運(yùn)行性能。
但是啊,如果我們能在程序啟動(dòng)的時(shí)候,就先創(chuàng)建一定數(shù)量的線程放在一個(gè)池子里,我們要用的話就去拿,用完了就再放到池子里,這樣就很好的避免了創(chuàng)建和銷毀線程的過程,這種方式比較友好;其中這個(gè)存放線程的池子就叫做線程池,接下來我們隨意看看線程池的用法:
順便一提,利用線程池執(zhí)行線程任務(wù)有兩種方式,一種是我們用的pool.execute(xxxx),另外一種是pool.submit(xxxx),用法和參數(shù)都一樣,區(qū)別是用submit()提交內(nèi)部其實(shí)還是調(diào)用execute(),而且還可以獲取線程執(zhí)行后的返回值,后面我們會分析到的;
線程池起到一個(gè)類似緩沖的作用,它可以對池子中的線程數(shù)目進(jìn)行控制,想想,假如我們程序直接創(chuàng)建線程那可能會由于創(chuàng)建線程太多導(dǎo)致jvm崩潰,但是我們有一個(gè)確定容量的池子,我們不用擔(dān)心這個(gè)池子會炸了,我們只需要從池子里拿就好了,至于拿不拿得到的問題后面我們會好好分析的;
3.4.看看Callable接口
這個(gè)接口干嘛的呢?有了Runnable接口了,還要這個(gè)接口干嘛?
不知道有沒有注意到那個(gè)Runnable接口的run()方法是沒有返回值的,也就是說我們只能把任務(wù)交給這個(gè)線程去做,但是做了之后有沒有成功,線程是否異常我們都是不知道的,于是才有了Callable接口,這個(gè)接口就是對Runnable接口的一個(gè)補(bǔ)充,這個(gè)接口的實(shí)現(xiàn)類中沒有run()方法,卻有一個(gè)call()方法用于執(zhí)行我們的任務(wù)邏輯,而且還能有返回值,并且能拋出異常等
順便一提,返回值已經(jīng)被封裝成一個(gè)Future<T>類型的了,我們只需要從這個(gè)Futrue中取到返回值就可以進(jìn)行后續(xù)操作了,有興趣的可以看看Futrue這個(gè)包裝類中有哪些方法可以試試,反正我暫時(shí)是沒什么興趣的。。。。
4.多線程下的jvm內(nèi)存結(jié)構(gòu)
初學(xué)者學(xué)多線程其實(shí)最迷糊的一點(diǎn)就是多線程的程序中,jvm是什么樣的啊?還是向以前那樣分嗎?到底多線程這個(gè)東西在jvm中是怎么樣存在的呢?下面我們就來簡單看看;
我自己總結(jié)的一句話:一個(gè)線程一個(gè)棧,一個(gè)方法一個(gè)幀;
這句話的意思就是每創(chuàng)建一個(gè)線程就會創(chuàng)建一個(gè)棧,每調(diào)用一個(gè)方法就會在棧中壓入一個(gè)棧幀;
其實(shí)java棧是一個(gè)動(dòng)態(tài)的東西,不像我們前面看jvm內(nèi)存結(jié)構(gòu)就是一大塊java棧,里面可以有很多塊,一個(gè)線程一塊,總共合起來叫做java棧,我繼續(xù)來畫一個(gè)丑陋的圖看一看:
其實(shí)可以看到在我們java程序中用多線程的話,那么每一個(gè)線程都會創(chuàng)建一個(gè)棧,同時(shí)每個(gè)線程都有自己的PC計(jì)數(shù)器,而且每個(gè)棧都是該線程私有的,別的線程不能訪問;但是在java堆和方法區(qū)中的數(shù)據(jù),是所有線程共享的,由于所有的線程都能夠使用共享區(qū)的數(shù)據(jù),假設(shè)一個(gè)線程拿到堆中的一個(gè)A對象進(jìn)行修改但是需要的時(shí)間比較長,此時(shí)另一個(gè)線程也要拿到A對象進(jìn)行判斷然后做一些操作,這個(gè)時(shí)候就會出問題,因?yàn)榍耙粋€(gè)線程修改的數(shù)據(jù)還沒有同步過來,后面線程拿到的是舊數(shù)據(jù),這個(gè)問題就是多線程的同步問題,后面我們慢慢分析;
5.總結(jié)
其實(shí)初學(xué)者覺得多線程比較難,主要是因?yàn)椴焕斫舛嗑€程到底是什么?我們可以把多線程代碼用這種奇葩的形式看是不是明顯多了,其中主線程最開始執(zhí)行并創(chuàng)建自己的棧和PC計(jì)數(shù)器,一直到創(chuàng)建其他的三個(gè)線程并把分別調(diào)用start()方法的時(shí)候,這些線程會隨機(jī)由CPU執(zhí)行以及切換線程,并且各個(gè)線程都會創(chuàng)建自己的棧和PC計(jì)數(shù)器;而堆和方法區(qū)的數(shù)據(jù)是共享的,這會導(dǎo)致出現(xiàn)線程同步問題;
注意:千萬不要覺得主線程比其他創(chuàng)建的線程要特殊,除了我們程序是由主線程開始之外,這些線程都是出于同一地位,很有可能首先是主線程執(zhí)行完畢,然后再執(zhí)行1、2、3這三個(gè)線程哦~~
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。