線程,有時(shí)被稱為輕量級(jí)進(jìn)程(Lightweight Process,LWP),是程序執(zhí)行流的最小單元。一個(gè)標(biāo)準(zhǔn)的線程由線程ID,當(dāng)前指令指針(PC),寄存器集合和堆棧組成。另外,線程是進(jìn)程中的一個(gè)實(shí)體,是被系統(tǒng)獨(dú)立調(diào)度和分派的基本單位,線程自己不擁有系統(tǒng)資源,只擁有一點(diǎn)兒在運(yùn)行中必不可少的資源,但它可與同屬一個(gè)進(jìn)程的其它線程共享進(jìn)程所擁有的全部資源。一個(gè)線程可以創(chuàng)建和撤消另一個(gè)線程,同一進(jìn)程中的多個(gè)線程之間可以并發(fā)執(zhí)行。由于線程之間的相互制約,致使線程在運(yùn)行中呈現(xiàn)出間斷性。線程也有就緒、阻塞和運(yùn)行三種基本狀態(tài)。
就緒狀態(tài)是指線程具備運(yùn)行的所有條件,邏輯上可以運(yùn)行,在等待處理機(jī);運(yùn)行狀態(tài)是指線程占有處理機(jī)正在運(yùn)行;阻塞狀態(tài)是指線程在等待一個(gè)事件(如某個(gè)信號(hào)量),邏輯上不可執(zhí)行。每一個(gè)程序都至少有一個(gè)線程,若程序只有一個(gè)線程,那就是程序本身。
線程特點(diǎn)
在多線程OS中,通常是在一個(gè)進(jìn)程中包括多個(gè)線程,每個(gè)線程都是作為利用CPU的基本單位,是花費(fèi)最小開銷的實(shí)體。線程具有以下屬性。
1)輕型實(shí)體
線程中的實(shí)體基本上不擁有系統(tǒng)資源,只是有一點(diǎn)必不可少的、能保證獨(dú)立運(yùn)行的資源。
線程的實(shí)體包括程序、數(shù)據(jù)和TCB。線程是動(dòng)態(tài)概念,它的動(dòng)態(tài)特性由線程控制塊TCB(Thread Control Block)描述。TCB包括以下信息:
?。?)線程狀態(tài)。
?。?)當(dāng)線程不運(yùn)行時(shí),被保存的現(xiàn)場資源。
?。?)一組執(zhí)行堆棧。
?。?)存放每個(gè)線程的局部變量主存區(qū)。
?。?)訪問同一個(gè)進(jìn)程中的主存和其它資源。
用于指示被執(zhí)行指令序列的程序計(jì)數(shù)器、保留局部變量、少數(shù)狀態(tài)參數(shù)和返回地址等的一組寄存器和堆棧。
2)獨(dú)立調(diào)度和分派的基本單位。
在多線程OS中,線程是能獨(dú)立運(yùn)行的基本單位,因而也是獨(dú)立調(diào)度和分派的基本單位。由于線程很“輕”,故線程的切換非常迅速且開銷?。ㄔ谕贿M(jìn)程中的)。
3)可并發(fā)執(zhí)行。
在一個(gè)進(jìn)程中的多個(gè)線程之間,可以并發(fā)執(zhí)行,甚至允許在一個(gè)進(jìn)程中所有線程都能并發(fā)執(zhí)行;同樣,不同進(jìn)程中的線程也能并發(fā)執(zhí)行,充分利用和發(fā)揮了處理機(jī)與外圍設(shè)備并行工作的能力。
4)共享進(jìn)程資源。
線程
在同一進(jìn)程中的各個(gè)線程,都可以共享該進(jìn)程所擁有的資源,這首先表現(xiàn)在:所有線程都具有相同的地址空間(進(jìn)程的地址空間),這意味著,線程可以訪問該地址空間的每一個(gè)虛地址;此外,還可以訪問進(jìn)程所擁有的已打開文件、定時(shí)器、信號(hào)量機(jī)構(gòu)等。由于同一個(gè)進(jìn)程內(nèi)的線程共享內(nèi)存和文件,所以線程之間互相通信不必調(diào)用內(nèi)核。
一。什么時(shí)候會(huì)出現(xiàn)線程安全問題?
在單線程中不會(huì)出現(xiàn)線程安全問題,而在多線程編程中,有可能會(huì)出現(xiàn)同時(shí)訪問同一個(gè)資源的情況,這種資源可以是各種類型的的資源:
一個(gè)變量、一個(gè)對(duì)象、一個(gè)文件、一個(gè)數(shù)據(jù)庫表等,而當(dāng)多個(gè)線程同時(shí)訪問同一個(gè)資源的時(shí)候,就會(huì)存在一個(gè)問題:
由于每個(gè)線程執(zhí)行的過程是不可控的,所以很可能導(dǎo)致最終的結(jié)果與實(shí)際上的愿望相違背或者直接導(dǎo)致程序出錯(cuò)。
舉個(gè)簡單的例子:
現(xiàn)在有兩個(gè)線程分別從網(wǎng)絡(luò)上讀取數(shù)據(jù),然后插入一張數(shù)據(jù)庫表中,要求不能插入重復(fù)的數(shù)據(jù)。
那么必然在插入數(shù)據(jù)的過程中存在兩個(gè)操作:
1)檢查數(shù)據(jù)庫中是否存在該條數(shù)據(jù);
2)如果存在,則不插入;如果不存在,則插入到數(shù)據(jù)庫中。
假如兩個(gè)線程分別用thread-1和thread-2表示,某一時(shí)刻,thread-1和thread-2都讀取到了數(shù)據(jù)X,那么可能會(huì)發(fā)生這種情況:
thread-1去檢查數(shù)據(jù)庫中是否存在數(shù)據(jù)X,然后thread-2也接著去檢查數(shù)據(jù)庫中是否存在數(shù)據(jù)X。
結(jié)果兩個(gè)線程檢查的結(jié)果都是數(shù)據(jù)庫中不存在數(shù)據(jù)X,那么兩個(gè)線程都分別將數(shù)據(jù)X插入數(shù)據(jù)庫表當(dāng)中。
這個(gè)就是線程安全問題,即多個(gè)線程同時(shí)訪問一個(gè)資源時(shí),會(huì)導(dǎo)致程序運(yùn)行結(jié)果并不是想看到的結(jié)果。
這里面,這個(gè)資源被稱為:臨界資源(也有稱為共享資源)。
也就是說,當(dāng)多個(gè)線程同時(shí)訪問臨界資源(一個(gè)對(duì)象,對(duì)象中的屬性,一個(gè)文件,一個(gè)數(shù)據(jù)庫等)時(shí),就可能會(huì)產(chǎn)生線程安全問題。
不過,當(dāng)多個(gè)線程執(zhí)行一個(gè)方法,方法內(nèi)部的局部變量并不是臨界資源,因?yàn)榉椒ㄊ窃跅I蠄?zhí)行的,而Java棧是線程私有的,因此不會(huì)產(chǎn)生線程安全問題。
二。如何解決線程安全問題?
基本上所有的并發(fā)模式在解決線程安全問題時(shí),都采用“序列化訪問臨界資源”的方案,即在同一時(shí)刻,只能有一個(gè)線程訪問臨界資源,也稱作同步互斥訪問。
通常來說,是在訪問臨界資源的代碼前面加上一個(gè)鎖,當(dāng)訪問完臨界資源后釋放鎖,讓其他線程繼續(xù)訪問。
評(píng)論