進(jìn)程&&線(xiàn)程:
一個(gè)進(jìn)程中可以有多個(gè)線(xiàn)程稀余、
java虛擬機(jī)啟動(dòng)的時(shí)候會(huì)有一個(gè)進(jìn)程,至少有一個(gè)線(xiàn)程負(fù)責(zé)java程序的運(yùn)行趋翻,而且這個(gè)線(xiàn)程存在于main方法中睛琳,稱(chēng)為主線(xiàn)程。
更細(xì)節(jié)的說(shuō)踏烙,有兩個(gè)線(xiàn)程:至少還應(yīng)該有一個(gè)負(fù)責(zé)垃圾回收师骗。
多線(xiàn)程的出現(xiàn)可以讓程序中不同的部分出現(xiàn)同時(shí)運(yùn)行的效果。
明確:在某一時(shí)刻讨惩,只能有一個(gè)程序運(yùn)行辟癌,多核cpu除外。cpu在做著快速切換荐捻,至于執(zhí)行多久黍少,未知
這就是多線(xiàn)程的一個(gè)特性:隨機(jī)性。
如何在自定義的代碼中自定義一個(gè)線(xiàn)程(運(yùn)行單元)呢处面?
方式一
Thread:用于描述控制單元這一類(lèi)事物的對(duì)象厂置。
1、創(chuàng)建一個(gè)類(lèi)鸳君,繼承Thread類(lèi)农渊,重寫(xiě)里面的run()方法
public class MyThread extends Thread{
public void run(){}
}
2、創(chuàng)建此類(lèi)對(duì)象或颊,用其調(diào)用start()方法砸紊。
(該方法的作用:?jiǎn)?dòng)新線(xiàn)程,并調(diào)用run()方法囱挑。主角是run()方法醉顽,因?yàn)樾戮€(xiàn)程需要被執(zhí)行的代碼存放在run方法里面,所以在創(chuàng)建線(xiàn)程對(duì)象時(shí)平挑,就必須明確要運(yùn)行哪些代碼游添。)
new一個(gè)Thread系草,就是一個(gè)新的線(xiàn)程。new MyThread().start;
為什么要重寫(xiě)run()方法呢唆涝?
Thread類(lèi)用于描述線(xiàn)程找都,定義了一個(gè)功能,用于存儲(chǔ)要運(yùn)行的代碼廊酣,該“存儲(chǔ)功能”就是run()方法能耻。
(聯(lián)系:主線(xiàn)程存放在main方法中)
方式二:
用一個(gè)實(shí)現(xiàn)類(lèi)實(shí)現(xiàn)Runnable接口,重寫(xiě)里面的run()方法亡驰。將Runnable接口的實(shí)現(xiàn)類(lèi)的引用以構(gòu)造函數(shù)實(shí)際參數(shù)的形式傳給Thread類(lèi)的引用晓猛。
為什么要將Runnable引用傳給Thread()的構(gòu)造函數(shù)呢?
因?yàn)樽远x的run()方法所屬的對(duì)象是Runnable接口的實(shí)現(xiàn)類(lèi)對(duì)象凡辱,想讓線(xiàn)程去執(zhí)行指定對(duì)象的run方法戒职,就必須明確該類(lèi)所屬的對(duì)象。
----------------
在 Runnable 接口中并沒(méi)有 start()方法透乾,只有run()方法
調(diào)用的是Thread類(lèi)的start()方法開(kāi)啟線(xiàn)程和Runnable接口實(shí)現(xiàn)類(lèi)的run()方法洪燥。
public MyRunnable implements Runnable{
public void run(){}
}
new Thread(new MyRunnable()).start;
要記住:
兩種方法乳乌,第一種是繼承方式蚓曼,第二中是實(shí)現(xiàn)方式,兩種方式有什么區(qū)別钦扭?
存放代碼的位置不一樣:
繼承Thread,代碼存放在Thread子類(lèi)復(fù)寫(xiě)的run()方法中床绪,
實(shí)現(xiàn)Runnable():代碼存放在Runnable的實(shí)現(xiàn)類(lèi)復(fù)寫(xiě)的run()方法中客情。
實(shí)現(xiàn)方式避免了單繼承的局限性,在定義新線(xiàn)程的時(shí)候癞己,建議使用實(shí)現(xiàn)方式膀斋。
線(xiàn)程的存在形式(狀態(tài)):
初始---就緒----運(yùn)行-----堵塞------死亡
堵塞:在可執(zhí)行狀態(tài)下,如
果調(diào)用 sleep()痹雅、 suspend()仰担、 wait()等方法,線(xiàn)程都將進(jìn)入堵塞狀態(tài)绩社。堵塞時(shí)摔蓝,線(xiàn)程不能進(jìn)入排隊(duì)隊(duì)列,只有當(dāng)引起堵塞的原因被消除后愉耙,線(xiàn)程才可以轉(zhuǎn)入就緒狀態(tài)贮尉。
死亡:調(diào)用 stop()方法時(shí)或 run()方法執(zhí)行結(jié)束后,線(xiàn)程即處于死亡狀態(tài)
線(xiàn)程都有自己默認(rèn)的名稱(chēng)
默認(rèn)是從Thread 0開(kāi)始的朴沿。
static Thread currentThread():獲取當(dāng)前線(xiàn)程對(duì)象
getName():獲取線(xiàn)程名稱(chēng)
currentThread().getName():獲取當(dāng)前線(xiàn)程的名稱(chēng)
多線(xiàn)程安全問(wèn)題的產(chǎn)生
在多條語(yǔ)句在執(zhí)行同一條共享數(shù)據(jù)時(shí)猜谚,還未執(zhí)行完败砂,另一個(gè)線(xiàn)程參與進(jìn)來(lái),導(dǎo)致共享數(shù)據(jù)的錯(cuò)誤魏铅。
解決辦法
對(duì)多條操作共享數(shù)據(jù)的語(yǔ)句昌犹,只讓一個(gè)執(zhí)行完。執(zhí)行過(guò)程中不讓其它線(xiàn)程參與進(jìn)來(lái)览芳。
1斜姥、同步代碼塊:哪些代碼需要同步,就看哪些代碼在操作共享數(shù)據(jù)路操。
Synchronized (對(duì)象){
需要同步的代碼
}
2疾渴、非靜態(tài)同步方法:
訪(fǎng)問(wèn)權(quán)限 ?synchronized 返回值 方法名(參數(shù)列表){
需要被同步的代碼
}
非靜態(tài)同步方法的同步對(duì)象就是當(dāng)前對(duì)象(為了能鎖住,必須只能有一個(gè)runnable實(shí)現(xiàn)類(lèi)對(duì)象屯仗。此時(shí)資源是共享的搞坝,因?yàn)槭峭粋€(gè)實(shí)例對(duì)象。)魁袜。
3桩撮、靜態(tài)同步方法:
含有static修飾的同步方法(static synchronized)
靜態(tài)同步方法的同步對(duì)象是類(lèi)對(duì)象(可以是不同的Runnable實(shí)例對(duì)象,依然能鎖住峰弹。此時(shí)的資源也是共享的店量,但是是因?yàn)楸籹tatic修飾了)。
同步的前提:
1鞠呈、必須是兩個(gè)或者兩個(gè)以上的線(xiàn)程融师。
2、必須多個(gè)線(xiàn)程同時(shí)使用同一個(gè)鎖蚁吝。
必須保證同步中只能有一個(gè)線(xiàn)程在運(yùn)行旱爆。
注意
獲得CPU執(zhí)行權(quán)的線(xiàn)程即使未獲得鎖也依然占用著資源。在獲得執(zhí)行權(quán)后還要判斷是否有鎖窘茁。所以會(huì)消耗資源怀伦。但是在允許范圍內(nèi)。
好處:解決了多線(xiàn)程的安全問(wèn)題山林。
弊端:多個(gè)線(xiàn)程需要判斷鎖房待,比較消耗資源。
-------------------------------------------------------------------------------------
如何找出多線(xiàn)程中出現(xiàn)的安全問(wèn)題:
1驼抹、明確哪些代碼是多線(xiàn)程運(yùn)行代碼
2桑孩、明確哪些是共享數(shù)據(jù)
3、明確哪些代碼是操作共享數(shù)據(jù)的
死鎖問(wèn)題
一旦有多個(gè)進(jìn)程框冀,且它們都要爭(zhēng)用對(duì)多個(gè)鎖的獨(dú)占訪(fǎng)問(wèn)洼怔,那么就有可能發(fā)生死鎖。
如果有一組進(jìn)程或線(xiàn)程左驾,其中每個(gè)都在等待一個(gè)只有其它進(jìn)程或線(xiàn)程才可以執(zhí)行的操
作镣隶,那么就稱(chēng)它們被死鎖了极谊。
要避免死鎖,應(yīng)該確保在獲取多個(gè)鎖時(shí)安岂,在所有的線(xiàn)程中都以相同的順序獲取鎖轻猖。