一叔遂、重點知識
thread重寫了tostring方法
getid()獲得線程的標(biāo)識符 從一開始,是唯一的,在生命周期中保持不變锋勺,線程死亡后此標(biāo)識符可以被重用
main的標(biāo)識符為1
通過線程標(biāo)識符变丧,可以對線程進(jìn)行區(qū)分芽狗。
sleep使當(dāng)前正在執(zhí)行的線程進(jìn)入睡眠和誰調(diào)用這個方法沒有關(guān)系
sleep是醒了之后繼續(xù)執(zhí)行,不是重新執(zhí)行一次痒蓬。
反射中創(chuàng)建運行時類時會報編譯時異常InstantiationException
守護(hù)線程(setDaemon)守護(hù)前臺線程(就是我們ide控制的線程)垃圾回收機制就是個守護(hù)進(jìn)程
前臺線程結(jié)束童擎,后臺線程就結(jié)束了
線程死亡后會被銷毀
原子性操作:代碼一次性被一個線程執(zhí)行完,不能被其他線程插入
方法要想執(zhí)行得放在椆ド梗空間中
棧是用來執(zhí)行方法的
jvm虛擬機啟動后最少存在兩個線程顾复,實際情況啟動了很多線程,但最少存在兩個鲁捏,主線程和gc垃圾回收線程
線程并不是程序開的芯砸,而是程序申請開線程,操作系統(tǒng)給程序開的線程给梅,線程的管理權(quán)和監(jiān)測權(quán)歸操作系統(tǒng)所有
一個新的線程開啟之后不會阻塞當(dāng)前代碼的執(zhí)行
多線程的優(yōu)勢
https://blog.csdn.net/stonesing/article/details/49746661
多線程的最大意義是節(jié)省等待時間假丧,在一個線程需要等待時可以執(zhí)行其他線程
如果程序中沒有等待時間,多線程將變得毫無意義
線程越多动羽,效率越低包帚,系統(tǒng)需要維護(hù),監(jiān)測管理多線程运吓,所以會降低效率渴邦,浪費性能
就緒狀態(tài)是一定存在的
自己創(chuàng)建的線程的run方法中還可以創(chuàng)建線程
新的線程是老的線程創(chuàng)建的
主線程與其他線程的關(guān)系
https://www.cnblogs.com/qiumingcheng/p/8202393.html
當(dāng)非守護(hù)線程結(jié)束后,守護(hù)線程不是立即結(jié)束拘哨,他有一個檢測過程
程序運行到什么位置會失去執(zhí)行權(quán)是不固定的
最好不要讓線程共享數(shù)據(jù)谋梭,是解決多線程安全問題得最優(yōu)解
二、重點問題
同步監(jiān)視器的選取問題
自己定義的鎖記得千萬不能定義到run方法中倦青,不然每個run方法都會創(chuàng)建一個鎖.....字面常量也可以當(dāng)鎖瓮床,內(nèi)存中只有一份
繼承實現(xiàn)線程的時候,鎖可以在外部創(chuàng)造,然后在構(gòu)造方法中傳入鎖纤垂,這樣就能保證用的是一把鎖
三矾策、課堂知識
3.1、線程的兩種啟動方式
Thread類:JDK提供好的類峭沦,用于表示一個線程對象贾虽。實現(xiàn)了Runnable接口
Runnable接口:定義了唯一的一個方法:run()——>線程體
方法一:直接繼承Thread類
step1:創(chuàng)建一個子類,來繼承Thread類
step2:重寫run()方法吼鱼,因為這是線程體:當(dāng)CPU調(diào)度執(zhí)行該線程的時候蓬豁,就要執(zhí)行的是run()方法中的代碼。
step3:創(chuàng)建該類的對象菇肃,表示一個線程地粪,調(diào)用start()進(jìn)而啟動這個線程。意味著該線程一切準(zhǔn)備就緒琐谤,隨時可以被CPU調(diào)度執(zhí)行蟆技。但是CPU并不是立即執(zhí)行,要看CPU自己是否調(diào)用了這個線程斗忌。
方法二:實現(xiàn)Runnable接口
step1:創(chuàng)建一個實現(xiàn)類质礼,實現(xiàn)Runnable接口
step2:重寫run()方法
step3:先創(chuàng)建該實現(xiàn)類對象,根據(jù)實現(xiàn)類對象再創(chuàng)建Thread對象织阳,然后啟動眶蕉。
Thread類的構(gòu)造方法:
Thread();//創(chuàng)建一個線程對象,執(zhí)行run()唧躲。線程的默認(rèn)名:Thread-0,1,2...
Thread(Runnable target);//創(chuàng)建一個線程對象造挽,指明了target,執(zhí)行的run是Runnable接口中弄痹。
Thread(String name);//創(chuàng)建一個線程饭入,并給起個名字
Thread(Runnable target,String name);
對比兩種創(chuàng)建并啟動線程的方式:
* 開發(fā)中:優(yōu)先選擇:實現(xiàn)Runnable接口的方式
* 原因:1. 實現(xiàn)的方式?jīng)]類的單繼承性的局限性
*? ? ? ? ? ? ?2. 實現(xiàn)的方式更適合來處理多個線程共享數(shù)據(jù)的情況。
* 聯(lián)系:public class Thread implements Runnable
* 相同點:兩種方式都需要重寫run(),將線程要執(zhí)行的邏輯聲明在run()中界酒。
? ? ? ? ? ? ? ? ?目前兩種方式圣拄,要想啟動線程嘴秸,都是調(diào)用的Thread類中的start()毁欣。
3.2、線程的常用方法
關(guān)于Thread類的常用方法:
1岳掐、獲取當(dāng)前的線程對象:由Thread類直接調(diào)用凭疮,獲取當(dāng)前正在被執(zhí)行的那個線程
static Thread currentThread() ;//返回對當(dāng)前正在執(zhí)行的線程對象的引用。?
2串述、線程的名字:當(dāng)一個線程創(chuàng)建的時候执解,如果沒有設(shè)置名稱:構(gòu)造方法設(shè)置,或者setName()設(shè)置。系統(tǒng)默認(rèn)的:Thread-0,Thread-1,Thread-2......
String getName()
返回此線程的名稱衰腌。
void setName(String name)
將此線程的名稱更改為等于參數(shù) name 新蟆。
3、線程的Id:每個線程創(chuàng)建的時候右蕊,由系統(tǒng)自動分配一個Id琼稻,long類型的數(shù)值,終身不變饶囚。從線程的出生到死亡帕翻。
? ? 該Id值,由系統(tǒng)自動分配萝风,程序員無法手動操作嘀掸。
long getId()
返回此線程的標(biāo)識符。
4规惰、線程的優(yōu)先級:priority
? ? System.out.println("最大優(yōu)先級:"+Thread.MAX_PRIORITY);//10
System.out.println("最小優(yōu)先級:"+Thread.MIN_PRIORITY);//1
System.out.println("正常優(yōu)先級:"+Thread.NORM_PRIORITY);//5
當(dāng)一個線程被創(chuàng)建的時候睬塌,由系統(tǒng)自動分配一個優(yōu)先級,固定都是正常優(yōu)先級:5
但是程序員可以根據(jù)需求歇万,手動調(diào)整線程的優(yōu)先級衫仑。
? ? int getPriority()
返回此線程的優(yōu)先級
? ? void setPriority(int newPriority)
更改此線程的優(yōu)先級。
? ? 優(yōu)先級別高堕花,被CPU調(diào)度執(zhí)行的機會就多文狱。但是不絕對。
? ? 優(yōu)先級別低缘挽,被CPU執(zhí)行的機會就少瞄崇,但是也不絕對。
5壕曼、線程的睡眠:
? ? static void sleep(long millis)
使當(dāng)前正在執(zhí)行的線程以指定的毫秒數(shù)暫停(暫時停止執(zhí)行)苏研,具體取決于系統(tǒng)定時器和調(diào)度程序的精度和準(zhǔn)確性。
? ? 靜態(tài)方法腮郊,應(yīng)該由類直接調(diào)用摹蘑,對象也可以調(diào)用,有坑:不是誰調(diào)用就誰睡轧飞,而是當(dāng)前正在執(zhí)行的線程進(jìn)入睡眠了衅鹿。和誰調(diào)用無關(guān)。
6过咬、線程合并
在線程a中調(diào)用線程b的join(),此時線程a就進(jìn)入阻塞狀態(tài)大渤,直到線程b完全執(zhí)行完以后,線程a才結(jié)束阻塞狀態(tài)掸绞。
等待這個線程死亡泵三。
? t1線程,t2線程,main線程
? ? t1,t2,main--->3條線程搶占資源
? ? 某一個時刻:main線程中:t1.join()烫幕,主線程要等待t1線程死亡之后再執(zhí)行
? ? t1俺抽,t2--->2條線程搶占資源,main等
? ? t1結(jié)束后较曼,main線程再執(zhí)行
7凌埂、守護(hù)線程
? ? setDaemon();
為前臺線程服務(wù),如果所有的前臺線程都結(jié)束了诗芜,那么守護(hù)線程也就結(jié)束了瞳抓。
GC:垃圾自動回收機制。JVM啟動后伏恐,創(chuàng)建主線程執(zhí)行main()的時候孩哑。。翠桦。隨之而創(chuàng)建并啟動的還有很多后臺線程横蜒,比如gc()
3.3、線程的狀態(tài)
線程的生命周期:
線程new出來:新建
準(zhǔn)備就緒销凑,啟動:start:就緒狀態(tài)
如果被CPU調(diào)度執(zhí)行:運行狀態(tài)丛晌,run()方法
阻塞狀態(tài):-->進(jìn)入就緒
出生-->就緒-->運行-->死亡
3.4、臨界資源的安全問題
概念:多個線程訪問共享的數(shù)據(jù)斗幼,臨界資源澎蛛。多個線程之間存在共享的數(shù)據(jù)。一條線程執(zhí)行過程中蜕窿,其他線程也可以訪問谋逻,可能會修改數(shù)據(jù)的值。造成的共享數(shù)據(jù)的不安全桐经。叫做臨界資源的安全問題毁兆。
3.5、同步synchronized
同步:原子性操作阴挣。同步起來的代碼气堕,一次只能被1個線程執(zhí)行完畢,這個過程中畔咧,不能被其他的線程插入執(zhí)行茎芭。
同步的原理:
對象的"互斥鎖"。每個對象都可以看做一個鎖盒卸。有兩種狀態(tài):打開(默認(rèn))骗爆,關(guān)閉。
鎖對象:多條線程功能訪問的同一個對象蔽介。
同步的方式一:同步代碼塊
*? synchronized(同步監(jiān)視器){
*? ? ? //需要被同步的代碼
*? }
*? 說明:1.操作共享數(shù)據(jù)的代碼,即為需要被同步的代碼。? -->不能包含代碼多了虹蓄,也不能包含代碼少了犀呼。
*? ? ? ? ? ? ?2.共享數(shù)據(jù):多個線程共同操作的變量。比如:ticket就是共享數(shù)據(jù)薇组。
*? ? ? ? ? ? ?3.同步監(jiān)視器外臂,俗稱:鎖。任何一個類的對象律胀,都可以充當(dāng)鎖宋光。
*? ? ? ? ? 要求:多個線程必須要共用同一把鎖。
*
* 補充:在實現(xiàn)Runnable接口創(chuàng)建多線程的方式中炭菌,我們可以考慮使用this充當(dāng)同步監(jiān)視器罪佳。
? ? ? 在繼承Thread類創(chuàng)建多線程的方式中,慎用this充當(dāng)同步監(jiān)視器黑低,考慮使用當(dāng)前類充當(dāng)同步監(jiān)視器赘艳。
*?同步的 方式二:同步方法
*? ? 如果操作共享數(shù)據(jù)的代碼完整的聲明在一個方法中,我們不妨將此方法聲明同步的克握。
*? 關(guān)于同步方法的總結(jié):
*? 1. 同步方法仍然涉及到同步監(jiān)視器蕾管,只是不需要我們顯式的聲明。
*? 2. 非靜態(tài)的同步方法菩暗,同步監(jiān)視器是:this
*? ? 靜態(tài)的同步方法掰曾,同步監(jiān)視器是:當(dāng)前類本身
死鎖:多個線程互相持有對象,僵持的現(xiàn)象停团。
解決死鎖:
1婴梧、減少成員變量的使用。
2客蹋、加大鎖的粒度塞蹭。不要鎖小對象,鎖大對象讶坯。
注意字面常量也能充當(dāng)鎖番电,他在內(nèi)存中也是唯一的