線程的概念:
每個(gè)正在系統(tǒng)上運(yùn)行的程序都是一個(gè)進(jìn)程。每個(gè)進(jìn)程包含一到多個(gè)線程。進(jìn)程也可能是整個(gè)程序或者是部分程序的動(dòng)態(tài)執(zhí)行。線程是一組指令的集合,或者是程序的特殊段,它可以在程序里獨(dú)立執(zhí)行。也可以把它理解為代碼運(yùn)行的上下文。所以線程基本上是輕量級(jí)的進(jìn)程,它負(fù)責(zé)在單個(gè)程序里執(zhí)行多任務(wù)。通常由操作系統(tǒng)負(fù)責(zé)多個(gè)線程的調(diào)度和執(zhí)行。
線程是程序中一個(gè)單一的順序控制流程.在單個(gè)程序中同時(shí)運(yùn)行多個(gè)線程完成不同的工作,稱為多線程.
線程和進(jìn)程的區(qū)別在于,子進(jìn)程和父進(jìn)程有不同的代碼和數(shù)據(jù)空間,而多個(gè)線程則共享數(shù)據(jù)空間,每個(gè)線程有自己的執(zhí)行堆棧和程序計(jì)數(shù)器為其執(zhí)行上下文.多線程主要是為了節(jié)約CPU時(shí)間,發(fā)揮利用,根據(jù)具體情況而定. 線程的運(yùn)行中需要使用計(jì)算機(jī)的內(nèi)存資源和CPU。
多線程的概念
多線程是指從軟件或者硬件上實(shí)現(xiàn)多個(gè)線程并發(fā)執(zhí)行的技術(shù).
多線程是為了同步完成多項(xiàng)任務(wù),不是為了提高運(yùn)行效率,而是為了提高資源使用效率來(lái)提高系統(tǒng)的效率。線程是在同一時(shí)間需要完成多項(xiàng)任務(wù)的時(shí)候?qū)崿F(xiàn)的。
最簡(jiǎn)單的比喻多線程就像火車(chē)的每一節(jié)車(chē)廂,而進(jìn)程則是火車(chē)。車(chē)廂離開(kāi)火車(chē)是無(wú)法跑動(dòng)的,同理火車(chē)也不可能只有一節(jié)車(chē)廂。多線程的出現(xiàn)就是為了提高效率。
如果你的應(yīng)用程序需要采取以下的操作,那么你盡可在編程的時(shí)候考慮多線程機(jī)制:
連續(xù)的操作,需要花費(fèi)忍無(wú)可忍的過(guò)長(zhǎng)時(shí)間才可能完成
并行計(jì)算
為了等待網(wǎng)絡(luò)、文件系統(tǒng)、用戶或其他I/O響應(yīng)而耗費(fèi)大量的執(zhí)行時(shí)間
所以說(shuō),在動(dòng)手之前,先保證自己的應(yīng)用程序中是否出現(xiàn)了以上3種情形。
為什么需要多線程(解釋何時(shí)考慮使用線程)
從用戶的角度考慮,就是為了得到更好的系統(tǒng)服務(wù);從程序自身的角度考慮,就是使目標(biāo)任務(wù)能夠盡可能快的完成,更有效的利用系統(tǒng)資源。綜合考慮,一般以下場(chǎng)合需要使用多線程:
程序包含復(fù)雜的計(jì)算任務(wù)時(shí),主要是利用多線程獲取更多的CPU時(shí)間(資源)。
處理速度較慢的外圍設(shè)備.比如:打印時(shí)。再比如網(wǎng)絡(luò)程序,涉及數(shù)據(jù)包的收發(fā),時(shí)間因素不定。使用獨(dú)立的線程處理這些任務(wù),可使程序無(wú)需專門(mén)等待結(jié)果。
程序設(shè)計(jì)自身的需要.WINDOWS系統(tǒng)是基于消息循環(huán)的搶占式多任務(wù)系統(tǒng),為使消息循環(huán)系統(tǒng)不至于阻塞,程序需要多個(gè)線程的來(lái)共同完成某些任務(wù)。
每個(gè)正在系統(tǒng)上運(yùn)行的程序都是一個(gè)進(jìn)程。每個(gè)進(jìn)程包含一到多個(gè)線程。進(jìn)程也可能是整個(gè)程序或者是部分程序的動(dòng)態(tài)執(zhí)行。線程是一組指令的集合,或者是程序的特殊段,它可以在程序里獨(dú)立執(zhí)行。也可以把它理解為代碼運(yùn)行的上下文。所以線程基本上是輕量級(jí)的進(jìn)程,它負(fù)責(zé)在單個(gè)程序里執(zhí)行多任務(wù)。通常由操作系統(tǒng)負(fù)責(zé)多個(gè)線程的調(diào)度和執(zhí)行
線程的優(yōu)先級(jí)
優(yōu)先級(jí)的取值為1-10(數(shù)值越高優(yōu)先級(jí)越高)。
Public final int getPriority();? 得到線程優(yōu)先級(jí)的數(shù)值。
Public final void setPriority(int newPriority);修改線程的優(yōu)先級(jí)。
注:優(yōu)先級(jí)高不代表該線程就一定先運(yùn)行,只能代表該線程先運(yùn)行的可能型比較大。
控制線程周期常用的方法
Wait()釋放CPU的執(zhí)行權(quán),釋放鎖。
Notify()回到wait前的狀態(tài)。
Yied()讓線程臨時(shí)暫停。(讓線程將資源釋放出來(lái))
Join()讓該線程強(qiáng)行加入執(zhí)行。
SetDaemon(true)設(shè)置該線程為后臺(tái)線程(當(dāng)前臺(tái)線程結(jié)束時(shí),后臺(tái)線程一定會(huì)一起結(jié)束)。
注:結(jié)束線程原理就是讓run方法結(jié)束,所以只要控制run的流程即可。
為什么要線程同步
線程間共享代碼和數(shù)據(jù)可以節(jié)省系統(tǒng)開(kāi)銷(xiāo),提高效率。但也同時(shí)會(huì)導(dǎo)致“數(shù)據(jù)訪問(wèn)沖突”。如何實(shí)現(xiàn)線程間有機(jī)交互,并確保共享資源在某時(shí)只能被一個(gè)線程訪問(wèn),就是線程同步。
多個(gè)線程間共享的數(shù)據(jù)稱為臨界資源。
多線程的同步與互斥:
方式一:鎖
在主線程中初始化鎖為解鎖狀態(tài)
pthread_mutex_init(&mutex, NULL);
在編譯時(shí)初始化鎖為解鎖狀態(tài)
鎖初始化 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
訪問(wèn)對(duì)象時(shí)的加鎖操作與解鎖操作
加鎖 pthread_mutex_lock(&mutex)
釋放鎖 pthread_mutex_unlock(&mutex)
互斥鎖
每個(gè)對(duì)象都對(duì)應(yīng)一個(gè)互斥鎖標(biāo)記,可以保證在某一時(shí)刻只能有一個(gè)線程訪問(wèn)該對(duì)象。
互斥鎖的關(guān)鍵字 synchronized 可以寫(xiě)在某個(gè)方法上(代表鎖調(diào)用該方法的對(duì)象);? 可以括在要鎖的語(yǔ)句外。
好處:解決了線程安全的問(wèn)題
弊端:降低了運(yùn)行效率(判斷鎖,且不能共享信息);容易出現(xiàn)死鎖。
死鎖:
兩個(gè)線程A,B用到同一個(gè)對(duì)象s(s為共享資源),且線程A在執(zhí)行中要用到B運(yùn)行后所創(chuàng)造條件。在這種前提下A先開(kāi)始運(yùn)行,進(jìn)入同步塊后,對(duì)象s被鎖定,接著線程A因等待B運(yùn)行結(jié)束而進(jìn)入阻塞狀態(tài),于是B開(kāi)始運(yùn)行,但因無(wú)法訪問(wèn)對(duì)象s,線程B也進(jìn)入阻塞狀態(tài),等待s被線程A解鎖。最終的結(jié)果:兩個(gè)線程互相等待,都無(wú)法運(yùn)行。
方式二:信號(hào)量
鎖有一個(gè)很明顯的缺點(diǎn),那就是它只有兩種狀態(tài):鎖定與不鎖定。
信號(hào)量本質(zhì)上是一個(gè)非負(fù)數(shù)的整數(shù)計(jì)數(shù)器,它也被用來(lái)控制對(duì)公共資源的訪問(wèn)。當(dāng)公共資源增加的時(shí)候,調(diào)用信號(hào)量增加函數(shù)sem_post()對(duì)其進(jìn)行增加,當(dāng)公共資源減少的時(shí)候,調(diào)用函數(shù)sem_wait()來(lái)減少信號(hào)量。其實(shí),我們是可以把鎖當(dāng)作一個(gè)0-1信號(hào)量的。
它們是在/usr/include/semaphore.h中進(jìn)行定義的,信號(hào)量的數(shù)據(jù)結(jié)構(gòu)為sem_t, 本質(zhì)上,它是一個(gè)long型整數(shù)
相關(guān)函數(shù)
在使用semaphore之前,我們需要先引入頭文件#include
初始化信號(hào)量:?int sem_init(sem_t *sem, int pshared, unsigned int value);
成功返回0,失敗返回-1
sem:指向信號(hào)量結(jié)構(gòu)的一個(gè)指針
pshared: 不是0的時(shí)候,該信號(hào)量在進(jìn)程間共享,否則只能為當(dāng)前進(jìn)程的所有線程們共享
value:信號(hào)量的初始值
信號(hào)量減1操作,當(dāng)sem=0的時(shí)候該函數(shù)會(huì)堵塞?int sem_wait(sem_t *sem);
成功返回0,失敗返回-1
參數(shù)
sem:指向信號(hào)量的一個(gè)指針
信號(hào)量加1操作?int sem_post(sem_t *sem);
參數(shù)與返回同上
銷(xiāo)毀信號(hào)量?int sem_destroy(sem_t *sem);
參數(shù)與返回同上
信號(hào)量和鎖的區(qū)別
信號(hào)量用在多線程多任務(wù)同步的,一個(gè)線程完成了某一個(gè)動(dòng)作就通過(guò)信號(hào)量告訴別的線程,別的線程再進(jìn)行某些動(dòng)作(大家都在semtake的時(shí)候,就阻塞在 哪里)。而互斥鎖是用在多線程多任務(wù)互斥的,一個(gè)線程占用了某一個(gè)資源,那么別的線程就無(wú)法訪問(wèn),直到這個(gè)線程unlock,其他的線程才開(kāi)始可以利用這 個(gè)資源。比如對(duì)全局變量的訪問(wèn),有時(shí)要加鎖,操作完了,在解鎖。有的時(shí)候鎖和信號(hào)量會(huì)同時(shí)使用的”
也就是說(shuō),信號(hào)量不一定是鎖定某一個(gè)資源,而是流程上的概念,比如:有A,B兩個(gè)線程,B線程要等A線程完成某一任務(wù)以后再進(jìn)行自己下面的步驟,這個(gè)任務(wù) 并不一定是鎖定某一資源,還可以是進(jìn)行一些計(jì)算或者數(shù)據(jù)處理之類。而線程互斥量則是“鎖住某一資源”的概念,在鎖定期間內(nèi),其他線程無(wú)法對(duì)被保護(hù)的數(shù)據(jù)進(jìn) 行操作。在有些情況下兩者可以互換。
兩者之間的區(qū)別:
作用域
信號(hào)量: 進(jìn)程間或線程間(Linux僅線程間的無(wú)名信號(hào)量pthread semaphore)
互斥鎖: 線程間
上鎖時(shí)?
信號(hào)量: 只要信號(hào)量的value大于0,其他線程就可以sem_wait成功,成功后信號(hào)量的value減一。若value值不大于0,則sem_wait使得線程阻塞,直到sem_post釋放后value值加一,但是sem_wait返回之前還是會(huì)將此value值減一
互斥鎖: 只要被鎖住,其他任何線程都不可以訪問(wèn)被保護(hù)的資源
以下是信號(hào)燈(量)的一些概念:
信號(hào)燈與互斥鎖和條件變量的主要不同在于”燈”的概念,燈亮則意味著資源可用,燈滅則意味著不可用。如果說(shuō)后兩中同步方式側(cè)重于”等待”操作,即資 源不可用的話,信號(hào)燈機(jī)制則側(cè)重于點(diǎn)燈,即告知資源可用;
沒(méi)有等待線程的解鎖或激發(fā)條件都是沒(méi)有意義的,而沒(méi)有等待燈亮的線程的點(diǎn)燈操作則有效,且能保持 燈亮狀態(tài)。當(dāng)然,這樣的操作原語(yǔ)也意味著更多的開(kāi)銷(xiāo)。
信號(hào)燈的應(yīng)用除了燈亮/燈滅這種二元燈以外,也可以采用大于1的燈數(shù),以表示資源數(shù)大于1,這時(shí)可以稱之為多元燈。
原子操作
在多進(jìn)程(線程)訪問(wèn)共享資源時(shí),能夠確保所有其他的進(jìn)程(線程)都不在同一時(shí)間內(nèi)訪問(wèn)相同的資源。原子操作(atomic operation)是不需要synchronized,這是Java多線程編程的老生常談了。所謂原子操作是指不會(huì)被線程調(diào)度機(jī)制打斷的操作;這種操作一旦開(kāi)始,就一直運(yùn)行到結(jié)束,中間不會(huì)有任何 context switch (切換到另一個(gè)線程)。通常所說(shuō)的原子操作包括對(duì)非long和double型的primitive進(jìn)行賦值,以及返回這兩者之外的primitive。之所以要把它們排除在外是因?yàn)樗鼈兌急容^大,而JVM的設(shè)計(jì)規(guī)范又沒(méi)有要求讀操作和賦值操作必須是原子操作(JVM可以試著去這么作,但并不保證)。
?
評(píng)論