多線程
1.程序與進(jìn)程:程序就是一堆靜態(tài)的代碼歪泳,存儲在硬盤上,程序如果不運(yùn)行露筒,本質(zhì)就是一個文件呐伞,程序一旦運(yùn)行產(chǎn)生進(jìn)程,進(jìn)程一直向前運(yùn)行慎式,直到進(jìn)程結(jié)束伶氢。
2.操作系統(tǒng)的發(fā)展:單任務(wù)操作系統(tǒng):一段時間只能運(yùn)行一個程序(任務(wù))CPU利用率極低。引入進(jìn)程的概念:把程序的一次運(yùn)行產(chǎn)生進(jìn)程(內(nèi)存空間瘪吏。資源癣防。程序的執(zhí)行堆棧)進(jìn)程作為操作系統(tǒng)分配資源的基本單位。多任務(wù)操作系統(tǒng):一臺電腦就一個CPU掌眠,多個任務(wù)輪流使用CPU蕾盯,從宏觀上看,一段時間有多個任務(wù)正在運(yùn)行蓝丙,從微觀上看级遭,一個車時間點(diǎn)只有一個任務(wù)在運(yùn)行。CPU時間片:多個進(jìn)程通過CPU時間片輪轉(zhuǎn)實現(xiàn)多任務(wù)渺尘。這種現(xiàn)象稱為并發(fā)操作装畅。并行:一個時間段,多個任務(wù)同時運(yùn)行沧烈,多個CPU運(yùn)行各自的進(jìn)程。線程的引入解決實時性的問題像云。
3.進(jìn)程和線程的區(qū)別
? ?4.多線程實現(xiàn):thread類位于Java.lang包中锌雀,表示進(jìn)程中的執(zhí)行線程。實現(xiàn)多線程有兩種方式迅诬;1.繼承thread腋逆,main主線程和其它定義線程搶CPU執(zhí)行。多線程在提升CPU利用率的同時侈贷,增加程序的復(fù)雜度惩歉。2.實現(xiàn)runnable接口:共享資源
結(jié)論:1.多線程搶占CPU執(zhí)行,可能在任意位置被切換出去(掛起);
? ? ? ? ? ? 2.多線程搶占CPU后撑蚌,從上次掛起的位置開始執(zhí)行(先回復(fù)上次的執(zhí)行堆棧)
? ? ? ? ? ? 3.多線程都可以獨(dú)立運(yùn)行上遥,相互不干擾,多個線程都可以能訪問共享資源争涌,很容易導(dǎo)致數(shù)據(jù)錯亂粉楚。
5.現(xiàn)成的聲明周期
5.1新生狀態(tài):用new關(guān)鍵字建立一個線程后,該線程對象就處于新生狀態(tài)亮垫,處于新生狀態(tài)的線程有自己的內(nèi)存空間模软,通過吊用start()方法進(jìn)入就緒狀態(tài)。
5.2就緒狀態(tài):處于就緒狀態(tài)線程具備了運(yùn)行條件饮潦,但還沒分配到CPU燃异,處于線程就緒隊列,等待系統(tǒng)為其分配CPU继蜡,當(dāng)系統(tǒng)選定一個等待執(zhí)行的線程后回俐,他就會從就緒狀態(tài)進(jìn)入執(zhí)行狀態(tài),該動作稱為CPU調(diào)度
5.3運(yùn)行狀態(tài):在運(yùn)行狀態(tài)的線程執(zhí)行自己的run方法中代碼壹瘟,直到等待某資源而阻塞? ? 或完成任何而死亡鲫剿,如果在給定的時間片內(nèi)沒有執(zhí)行結(jié)束,就會被系統(tǒng)給換下來回到等待執(zhí)行狀態(tài)稻轨。
5.4阻塞狀態(tài):處于運(yùn)行狀態(tài)的線程在某些情況下灵莲,如執(zhí)行了sleep(睡眠)方法,或等待I/O設(shè)備等資源殴俱,將讓出CPU并暫時停止自己運(yùn)行政冻,進(jìn)入阻塞狀態(tài)。在阻塞狀態(tài)的線程不能進(jìn)入就緒隊列线欲。只有當(dāng)引起阻塞的原因消除時明场,如睡眠時間已到,或等待的I/O設(shè)備空閑下來李丰,線程便轉(zhuǎn)入就緒狀態(tài)苦锨,重新到就緒隊列中排隊等待,被系統(tǒng)選中后從原來停止的位置開始繼續(xù)執(zhí)行趴泌。
5.5死亡狀態(tài):死亡狀態(tài)是線程生命周期中的最后一個階段舟舒。線程死亡的原因有三個,一個是正常運(yùn)行的線程完成了它的全部工作嗜憔;另一個是線程被強(qiáng)制性地終止秃励,如通過stop方法來終止一個線程【不推薦使用】;三是線程拋出未捕獲的異常吉捶。
”6.線程常用方法:1.線程優(yōu)先級:線程優(yōu)先級高夺鲜,被CPU調(diào)度的幾率大皆尔,并不表示一定先運(yùn)行。
System.out.println(Thread.MIN_PRIORITY);
System.out.println(Thread.MAX_PRIORITY);
System.out.println(Thread.NORM_PRIORITY);
//主線程的優(yōu)先級(默認(rèn)優(yōu)先級)
System.out.println(Thread.currentThread().getPriority());
Thread01 t1?= new?Thread01();
//設(shè)置線程的優(yōu)先級
t1.setPriority(Thread.MAX_PRIORITY);
t1.start();
Thread01 t2?= new?Thread01();
//設(shè)置線程的優(yōu)先級
t2.setPriority(Thread.MIN_PRIORITY);
t2.start();
2.isalive:判斷? ? 線程是否處于活動狀態(tài)币励,線程調(diào)用start之后就處于活動狀態(tài)慷蠕。
Thread01 t1?= new?Thread01();
System.out.println(t1.isAlive());
//設(shè)置線程的優(yōu)先級
t1.setPriority(Thread.MAX_PRIORITY);
t1.start();
System.out.println(t1.isAlive());
3.join:調(diào)用該方法的線程強(qiáng)制執(zhí)行,其它線程處于阻塞狀態(tài)榄审,該線程執(zhí)行完畢后砌们,其它? ?線程再執(zhí)行join稱為線程的強(qiáng)制執(zhí)行,有可能被外界中斷產(chǎn)生InterruptedException 中斷異常搁进。
public?class?Test02 {
public?static?void?main(String[] args){
Thread02 t?= new?Thread02("線程A");
t.start();
for?(int?i?= 0; i?< 5; i++) {
if(i?== 2) {
try?{
t.join();
} catch?(InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + "->"?+ i);
}
}
}
4.sleep:在指定的毫秒數(shù)內(nèi)讓當(dāng)前正在執(zhí)行的線程休眠浪感,休眠的線程進(jìn)入阻塞狀態(tài)
public?static?void?main(String[] args) {
Thread03 t?= new?Thread03("線程A");
t.start();
Thread mainThread?= Thread.currentThread();
System.out.println(mainThread.getName()+"即將進(jìn)入休眠");
try?{
Thread.sleep(5000);
} catch?(InterruptedException e) {
e.printStackTrace();
}
//中斷線程
t.interrupt();
System.out.println(mainThread.getName()+"休眠完成");
}
5.yield:A hint to the scheduler that the current thread is willing to yield its current use of a processor. The scheduler is free to ignore this hint.當(dāng)前線程給cpu調(diào)度器一個暗示,暗示其想禮讓一次其擁有的cpu饼问,CPU調(diào)度者也可以狐貍這次暗示影兽。此時當(dāng)前線程進(jìn)入就緒狀態(tài)。
public?static?void?main(String[] args) {
Thread mainThread?= Thread.currentThread();
Thread04 t?= new?Thread04("線程A");
t.start();
for?(int?i?= 0; i?< 5; i++) {
if?(i?== 2) {
// yield使當(dāng)前禮讓一次
Thread.yield();
}
System.out.println(mainThread.getName() + "->"?+ i);
}
}
6.線程的終止:目前而言莱革,不推薦使用stop直接終止線程峻堰。用interrupt()方法去中斷正在執(zhí)行的線程,而在線程內(nèi)部一定要寫捕獲中斷的異常盅视。通過異常處理機(jī)制正常結(jié)束線程捐名。
7.線程的安全問題:線程在執(zhí)行過程中,通過cpu的調(diào)度闹击,執(zhí)行軌跡不確定镶蹋,對共享資源的訪問很容易造成數(shù)據(jù)的錯誤。我們稱這個錯亂稱為線程安全問題赏半。
1.同步概念:原子性操作:一個操作要么一次性做完贺归,要么根本不開始,不存在中間狀態(tài)断箫。同步就是讓操作保持原子性拂酣!java提供兩種方式實現(xiàn)同步。1.同步代碼塊synchronized(obj){}中的obj稱為同步監(jiān)視器.同步代碼塊中同步監(jiān)視器可以是任何對象仲义,但是推薦使用共享資源作為同步監(jiān)視器
把所有的同步操作放到同步代碼塊中婶熬,
synchronized?(mutex) {
???// .. .
}
mutex稱為互斥鎖/同步鎖。對共享資源進(jìn)行加鎖實現(xiàn)同步埃撵。一般用共享資源作為同步鎖尸诽,也稱同步監(jiān)視器。
public?class?MyRun implements?Runnable {
//共享資源
private?int?count?= 5;
@Override
public?void?run() {
//模擬一個窗口5個人
for?(int?i?= 0; i?< 5; i++) {
//同步代碼塊
// mutex?互斥鎖
synchronized?(this) {
if?(count?> 0) {
try?{
Thread.sleep(3000);
count--;
} catch?(InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"賣出一張票盯另,還剩"?+ count?+ "張票");
2.同步方法:如果同步代碼(原子性)很多,可以考慮使用同步方法洲赵。把普通方法用?synchronized修飾鸳惯,同步方法的同步監(jiān)視器是this商蕴。
Public?class?MyRun implements?Runnable {
//共享資源
private?int?count?= 5;
@Override
public?void?run() {
//模擬一個窗口5個人
for?(int?i?= 0; i?< 5; i++) {
this.saleTicket();
}
}
//同步方法默認(rèn)對this加鎖
private?synchronized?void?saleTicket() {
if?(count?> 0) {
try?{
Thread.sleep(3000);
count--;
} catch?(InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"賣出一張票,還剩"?+ count?+ "張票");
8.死鎖:線程t1芝发,擁有A資源绪商,再次申請B資源,線程t2,擁有B資源辅鲸,再申請A資源格郁,t1因為沒有申請到B資源而進(jìn)入阻塞;t2因為沒有申請到A資源進(jìn)入阻塞独悴。此時兩個線程都處于阻塞狀態(tài)而不能正常結(jié)束例书,而此時cpu空轉(zhuǎn),這種情況稱為死鎖刻炒。