個(gè)人主頁(yè):兜里有顆棉花糖
歡迎 點(diǎn)贊?? 收藏? 留言? 加關(guān)注??本文由 兜里有顆棉花糖 原創(chuàng)
收錄于專欄【Java系列專欄】【JaveEE學(xué)習(xí)專欄】
本專欄旨在分享學(xué)習(xí)Java的一點(diǎn)學(xué)習(xí)心得,歡迎大家在評(píng)論區(qū)交流討論??
一、背景引入
在引入多線程之前,我們先來(lái)看一下進(jìn)程是為了干什么的,一句話總結(jié)就是:滿足"并發(fā)編程
"這樣的需求。
早些時(shí)候,CPU都是單核心的CPU,但是隨著技術(shù)工藝變得越來(lái)越強(qiáng),單核心CPU的性能的確是越來(lái)越強(qiáng)的,但是單核心CPU技術(shù)工藝研發(fā)到一定程度后就會(huì)遇到瓶頸,從而導(dǎo)致單核心CPU的研發(fā)變得非常緩慢。于是就就逐漸向多核心CPU的方向進(jìn)行發(fā)展研究。
好了,既然CPU逐漸變成了多核心,那么應(yīng)用程序也應(yīng)該做出相應(yīng)的調(diào)整來(lái)把多核心的CPU給利用起來(lái)(意思就是僅僅由硬件上的支撐是不夠的,我們還要由軟件上的配合),以免造成一核有難,多核圍觀的場(chǎng)景。
另外,早期的系統(tǒng)一般是單任務(wù)系統(tǒng),即在同一時(shí)刻只能使用一個(gè)程序,而無(wú)法在同一時(shí)刻內(nèi)使用多個(gè)程序,完成多個(gè)任務(wù)。而要完成多個(gè)任務(wù)的話,進(jìn)程就顯得特別關(guān)鍵。
二、線程的引入
多進(jìn)程能夠很好的實(shí)現(xiàn)并發(fā)編程的效果,但還由一個(gè)缺點(diǎn)就是進(jìn)程太重,即消耗資源更多,速度更慢。如果進(jìn)程的創(chuàng)建和銷毀比較少的話那還勉強(qiáng)可以,但是如果進(jìn)程的創(chuàng)建和銷毀非常頻繁的話,此時(shí)就會(huì)產(chǎn)生非常多的開(kāi)銷。開(kāi)銷大主要體現(xiàn)在需要給進(jìn)程分配資源,而給進(jìn)程分配資源的這一過(guò)程也是需要很多時(shí)間成本的。
于是就有人提出這樣一個(gè)解決方案:即在創(chuàng)建進(jìn)程的過(guò)程中,只分配簡(jiǎn)單的PCB(進(jìn)程控制塊),而不去分配后續(xù)需要的內(nèi)存硬盤資源,即把分配資源的時(shí)間省去,這樣既可以完成多并發(fā)編程的任務(wù)又能夠提升進(jìn)程創(chuàng)建銷毀的速度。
這樣一種只分配簡(jiǎn)單的PCB而不去分配后續(xù)需要的內(nèi)存硬盤資源的這樣一種方式稱為輕量級(jí)進(jìn)程
,也叫做線程
(thread
)。所以簡(jiǎn)單來(lái)說(shuō)線程就是只分配PCB,但不分配后續(xù)需要的內(nèi)存硬盤資源
。
但是線程總歸是要完成任務(wù)的,如果不去分配后續(xù)需要的內(nèi)存硬盤資源顯然是不可以的,于是就有人想出來(lái)這樣的一個(gè)辦法:在創(chuàng)建進(jìn)程的時(shí)候,先把后續(xù)需要的內(nèi)存資源分配好;后續(xù)創(chuàng)建線程的時(shí)候讓線程在進(jìn)程內(nèi)部直接復(fù)用先前進(jìn)程分配創(chuàng)建好的內(nèi)存資源(線程和進(jìn)程直接的關(guān)系:進(jìn)程包含線程)
另外,一個(gè)進(jìn)程至少要包含一個(gè)線程,最初進(jìn)程創(chuàng)建出來(lái)的時(shí)候,我們可以將這個(gè)進(jìn)程視為一個(gè)只包含一個(gè)線程的進(jìn)程(注意此時(shí)創(chuàng)建進(jìn)程的過(guò)程是需要分配資源的,同時(shí)第一個(gè)線程的創(chuàng)建開(kāi)銷是可能是比較大的
),我們后續(xù)再次創(chuàng)建線程的時(shí)候就不需要分配資源了,也就省去了分配資源的過(guò)程,因?yàn)橘Y源已經(jīng)是有了的。
小總結(jié)
好了,我們已經(jīng)知道多進(jìn)程已經(jīng)可以完成并發(fā)編程的任務(wù)了,但是由于進(jìn)程本身有些重,重就體現(xiàn)在進(jìn)程的創(chuàng)建和銷毀所需要的內(nèi)存硬盤資源開(kāi)銷有些大。
所以就引入了線程(也稱為輕量級(jí)進(jìn)程
)來(lái)更高的解決上述的問(wèn)題。進(jìn)程中的多個(gè)線程公共復(fù)用了進(jìn)程中的各種內(nèi)存硬盤等資源,同時(shí)這些進(jìn)程可以各自獨(dú)立的在CPU中進(jìn)行調(diào)度。所以這樣的話線程既可以完成并發(fā)編程的效果,又可以以輕量級(jí)的方式來(lái)完成各種任務(wù)。
另外,這里補(bǔ)充一個(gè)點(diǎn),進(jìn)程和線程是兩個(gè)不同的概念,在Linux中復(fù)用了PCB這個(gè)結(jié)構(gòu)體來(lái)描述線程,而在Windows中,描述進(jìn)程和線程是不同的結(jié)構(gòu)體。所以在Windows中,為了更好的描述進(jìn)程,有時(shí)一個(gè)PCB對(duì)應(yīng)一個(gè)線程,多個(gè)PCB對(duì)應(yīng)一個(gè)進(jìn)程(即可能是多一個(gè)PCB來(lái)描述一個(gè)進(jìn)程,一個(gè)PCB來(lái)描述一個(gè)線程)。
CPU的內(nèi)存指針和文件描述符表文件描述符表這兩個(gè)字段內(nèi)容在同一個(gè)進(jìn)程是一樣的,但是上下文信息、狀態(tài)、優(yōu)先級(jí)、記賬信息......等支持調(diào)度的屬性
,這些PCB就不一樣了(意思就是每個(gè)線程獨(dú)立去CPU上進(jìn)行調(diào)度,每個(gè)線程的上下文、狀態(tài)、優(yōu)先級(jí)、記賬信息等支持調(diào)度的屬性都有自已獨(dú)立的一份
)。
線程的引入的確能夠提高任務(wù)執(zhí)行的速度,同時(shí)隨著線程數(shù)量的增多任務(wù)執(zhí)行完成的速度當(dāng)然也會(huì)增快,但是線程的數(shù)量不能是無(wú)限制提高的(CPU的核心數(shù)的數(shù)量是有限的。),同時(shí)線程數(shù)量也不是越多越好,因?yàn)榫€程調(diào)度的開(kāi)銷有時(shí)會(huì)拖慢整個(gè)程序的效率。
兩句話總結(jié):
1.進(jìn)程是操作系統(tǒng)中進(jìn)行資源分配的基本單位。
2.線程是操作系統(tǒng)中進(jìn)程調(diào)度執(zhí)行的基本單位。
三、進(jìn)程和線程的關(guān)系(面試題)
1.進(jìn)程包含線程,都是實(shí)現(xiàn)并發(fā)編程的一種方式,但是由于多進(jìn)程的創(chuàng)建和銷毀的效率并沒(méi)有那么高,且線程比進(jìn)程更加輕量。所以就引入了多線程的概念。
2.進(jìn)程是系統(tǒng)分派資源的基本單位;線程是是系統(tǒng)調(diào)度的基本單位,創(chuàng)建進(jìn)程的時(shí)候已經(jīng)把分配資源(
這里的資源主要指的是虛擬地址空間和符表
)的工作做了,后續(xù)創(chuàng)建進(jìn)程的時(shí)候直接復(fù)用之前的資源即可。
3.每個(gè)進(jìn)程都有自己獨(dú)立的地址空間,彼此直接不會(huì)相會(huì)影響到,從而保證了每個(gè)進(jìn)程的獨(dú)立性,保證了數(shù)據(jù)穩(wěn)定性。
需要注意的是,如果多個(gè)組線程程共用了一份地址空間的話,一旦某個(gè)線程拋出異常的話,就可能會(huì)導(dǎo)致一整個(gè)進(jìn)程結(jié)束(所以多個(gè)線程之間是很容易相互影響的。 )。
四、第一個(gè)多線程程序
線程本身是操作系統(tǒng)的概念,而操作系統(tǒng)又提供了API來(lái)供我們使用線程,來(lái)完成多線程編程(在Linux中稱為pthread庫(kù)
,通過(guò)這個(gè)庫(kù)我們就可以完成多線程編程)。
而在Java中,把操作系統(tǒng)中的API進(jìn)程分裝,即提供了Thread類
來(lái)幫助哦我們來(lái)完成多線程編程。
Java標(biāo)準(zhǔn)庫(kù)中的Thread類來(lái)自于java.lang這個(gè)包
,而Java.lang這個(gè)包
不需要我們手動(dòng)的import
。
好了,光說(shuō)不練是不行的,接下來(lái)我們就來(lái)看一下我們的第一個(gè)線程程序,請(qǐng)看下圖:
package thread;
class MyThread extends Thread {
@Override
public void run() {
System.out.println("Hello,world!");
}
}
public class Demo01 {
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start();
}
}
現(xiàn)在我們來(lái)解釋上述的線程程序,上述代碼的MyThread類
中的run()方法
是重寫的Thread父類
中的run()方法
,這個(gè)run()方法
相當(dāng)于線程程序的入口,線程程序跑起來(lái)之后具體要干什么事情,都是通過(guò)這個(gè)run()方法
來(lái)進(jìn)行描述的。
myThread.start()
這個(gè)操作就是在創(chuàng)建一個(gè)線程(即在底層調(diào)用操作系統(tǒng)提供的創(chuàng)建線程的API,同時(shí)在操作系統(tǒng)內(nèi)核中創(chuàng)建出其對(duì)應(yīng)的PCB結(jié)構(gòu),并將其加入到鏈表中)。此時(shí)這個(gè)新創(chuàng)建出來(lái)的線程就會(huì)參與到cpu的調(diào)度中,接下來(lái)線程要執(zhí)行的任務(wù),就是run()方法
中的內(nèi)容。
接下來(lái)我如果注釋掉
myThread.start();
,即直接執(zhí)行MyThread類中的run()方法
,請(qǐng)看下圖:
我們來(lái)解釋上述程序,我們前面已經(jīng)知道,start方法
會(huì)去調(diào)用操作系統(tǒng)提供的創(chuàng)建線程的API,同時(shí)在操作系統(tǒng)內(nèi)核中創(chuàng)建出其所對(duì)應(yīng)的PCB,此時(shí)這個(gè)新創(chuàng)建出來(lái)的線程就會(huì)參與cpu調(diào)度
。
而我們直接運(yùn)行run方法的話,此時(shí)并沒(méi)有調(diào)用操作系統(tǒng)提供的創(chuàng)建線程的API,也就沒(méi)有創(chuàng)建出新的進(jìn)程。當(dāng)整個(gè)程序開(kāi)始運(yùn)行的時(shí)候,此時(shí)會(huì)先創(chuàng)建出來(lái)一個(gè)java進(jìn)程,這個(gè)java進(jìn)程中至少包含一個(gè)進(jìn)程,稱為主進(jìn)程,這個(gè)主進(jìn)程是負(fù)責(zé)執(zhí)行main()方法的線程。
每一個(gè)線程都能夠被獨(dú)立的進(jìn)行調(diào)用和執(zhí)行。
每個(gè)線程都能夠被獨(dú)立的進(jìn)行調(diào)用執(zhí)行
每個(gè)線程都能夠被獨(dú)立的進(jìn)行調(diào)用執(zhí)行
,現(xiàn)在我們來(lái)寫一個(gè)程序來(lái)體現(xiàn)這句話。請(qǐng)看:
運(yùn)行結(jié)果如下(由于程序是一個(gè)死循環(huán),所以這里只是截取了部分的程序運(yùn)行結(jié)果),如下圖:
我們可以看到程序一會(huì)執(zhí)行Hello,Linux!
一會(huì)執(zhí)行Hello,world!
解釋:start()是新線程執(zhí)行的入口,而主線程和新線程是并發(fā)執(zhí)行的一個(gè)關(guān)系。
先新創(chuàng)建出來(lái)的線程創(chuàng)建出來(lái)之后會(huì)執(zhí)行run()方法中的代碼,而main()方法則是繼續(xù)往后執(zhí)行。所以最終呈現(xiàn)出來(lái)的程序運(yùn)行效果就是一會(huì)執(zhí)行Hello,Linux!
,一會(huì)執(zhí)行Hello,world!
。
現(xiàn)在我們?cè)趤?lái)看一段程序,請(qǐng)看下圖:
我們可以看到上述代碼把myThread.start()
注釋掉了,相當(dāng)于我們并沒(méi)有創(chuàng)建線程,那么此時(shí)的話只有整個(gè)程序只有主線程這一個(gè)線程了。
分析到這里我們已經(jīng)知道整個(gè)程序只有線程(也就是主線程),那么執(zhí)行的run()方法
就是在主線程中去執(zhí)行的,因?yàn)檎麄€(gè)程序就只有一個(gè)線程(即主線程)。
總結(jié)一下:文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-751581.html
- 每個(gè)線程都是一個(gè)獨(dú)立的執(zhí)行流。
- 每個(gè)線程都可以執(zhí)行一段代碼。
- 每個(gè)線程之間是并發(fā)的關(guān)系。
好了,本文到這里就結(jié)束了,就到這里吧,再見(jiàn)啦友友們?。?!文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-751581.html
到了這里,關(guān)于【Java系列】詳解多線程(一)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!