一些基礎(chǔ)概念
程序(program):是為完成特定任務(wù)、用某種語(yǔ)言編寫(xiě)的一組指令的集合煌茴。即指一段靜態(tài)的代碼义辕,靜態(tài)對(duì)象
-
進(jìn)程(process):是程序的一次執(zhí)行過(guò)程,或是正在運(yùn)行的一個(gè)程序现拒。是一個(gè)動(dòng)態(tài)的過(guò)程:有它自身的產(chǎn)生辣垒、存在和消亡的過(guò)程∮∈撸——生命周期
- 進(jìn)程作為資源分配的單位勋桶,系統(tǒng)在運(yùn)行時(shí)會(huì)為每個(gè)進(jìn)程分配不同的內(nèi)存區(qū)域
-
線(xiàn)程(thread):進(jìn)程可進(jìn)一步細(xì)化為線(xiàn)程,是一個(gè)程序內(nèi)部的一條執(zhí)行路徑.若一個(gè)進(jìn)程同一時(shí)間 并行執(zhí)行多個(gè)線(xiàn)程侥猬,就是支持多線(xiàn)程的.
- 線(xiàn)程作為調(diào)度和執(zhí)行的單位例驹,每個(gè)線(xiàn)程擁有獨(dú)立的運(yùn)行棧和程序計(jì)數(shù)器(pc)
-
并行與并發(fā)
并行:多個(gè)CPU同時(shí)執(zhí)行多個(gè)任務(wù)
并發(fā):一個(gè)CPU(采用時(shí)間片)同時(shí)執(zhí)行多個(gè)任務(wù)
Thread類(lèi)
構(gòu)造器
Thread() :創(chuàng)建新的Thread對(duì)象
Thread(String threadname): 創(chuàng)建線(xiàn)程并指定線(xiàn)程實(shí)例名
Thread(Runnable target) :指定創(chuàng)建線(xiàn)程的目標(biāo)對(duì)象,它實(shí)現(xiàn)了Runnable接 口中的run方法
Thread(Runnable target, String name) :創(chuàng)建新的Thread對(duì)象
特性
1.每個(gè)線(xiàn)程都是通過(guò)某個(gè)特定Thread對(duì)象的run()方法來(lái)完成操作的退唠,經(jīng)常把run()方法的主體稱(chēng)為 線(xiàn)程體
通過(guò)該Thread對(duì)象的start()方法來(lái)啟動(dòng)這個(gè)線(xiàn)程鹃锈,而非直接調(diào)用run()
有關(guān)方法
void start(): 啟動(dòng)線(xiàn)程,并執(zhí)行對(duì)象的run()方法
run(): 線(xiàn)程在被調(diào)度時(shí)執(zhí)行的操作
String getName(): 返回線(xiàn)程的名稱(chēng)
void setName(String name):設(shè)置該線(xiàn)程名稱(chēng)
static Thread currentThread(): 返回當(dāng)前線(xiàn)程瞧预。在Thread子類(lèi)中就是this屎债,通常用于主線(xiàn)程和Runnable實(shí)現(xiàn)類(lèi)
static void yield(): 線(xiàn)程讓步,暫停當(dāng)前正在執(zhí)行的線(xiàn)程垢油,把執(zhí)行機(jī)會(huì)讓給優(yōu)先級(jí)相同或更高的線(xiàn)程盆驹,若隊(duì)列中沒(méi)有同優(yōu)先級(jí)的線(xiàn)程,忽略此方法
join() : 當(dāng)某個(gè)程序執(zhí)行流中調(diào)用其他線(xiàn)程的 join() 方法時(shí)秸苗,調(diào)用線(xiàn)程將被阻塞召娜,直到 join() 方法加入的 join 線(xiàn)程執(zhí)行完為止,低優(yōu)先級(jí)的線(xiàn)程也可以獲得執(zhí)行
static void sleep(long millis) :(指定時(shí)間:毫秒)惊楼,令當(dāng)前活動(dòng)線(xiàn)程在指定時(shí)間段內(nèi)放棄對(duì)CPU控制,使其他線(xiàn)程有機(jī)會(huì)被執(zhí)行,時(shí)間到后重排隊(duì)玖瘸。拋出
InterruptedException
異常stop(): 強(qiáng)制線(xiàn)程生命期結(jié)束,不推薦使用
boolean isAlive(): 返回boolean檀咙,判斷線(xiàn)程是否還活著
創(chuàng)建線(xiàn)程的兩種方式
繼承Thread類(lèi)
-
用法:
1.定義子類(lèi)繼承Thread類(lèi)
2.子類(lèi)中重寫(xiě)Thread類(lèi)中的run方法
3.創(chuàng)建Thread子類(lèi)對(duì)象雅倒,即創(chuàng)建了線(xiàn)程對(duì)象
4.調(diào)用線(xiàn)程對(duì)象start方法:?jiǎn)?dòng)線(xiàn)程,調(diào)用run方法
-
說(shuō)明:
1.如果自己手動(dòng)調(diào)用run()方法弧可,那么就只是普通方法蔑匣,沒(méi)有啟動(dòng)多線(xiàn)程模式
2.run()方法由JVM調(diào)用,什么時(shí)候調(diào)用,執(zhí)行的過(guò)程控制都有操作系統(tǒng)的CPU 調(diào)度決定
3.想要啟動(dòng)多線(xiàn)程裁良,必須調(diào)用start方法
4.一個(gè)線(xiàn)程對(duì)象只能調(diào)用一次start()方法啟動(dòng)凿将,如果重復(fù)調(diào)用了,則將拋出以上 的異常
IllegalThreadStateException
實(shí)現(xiàn)Runnable接口
-
用法:
1.定義子類(lèi)价脾,實(shí)現(xiàn)Runnable接口
2.子類(lèi)中重寫(xiě)Runnable接口中的run方法
3.通過(guò)Thread類(lèi)含參構(gòu)造器創(chuàng)建線(xiàn)程對(duì)象
4.將Runnable接口的子類(lèi)對(duì)象作為實(shí)際參數(shù)傳遞給Thread類(lèi)的構(gòu)造器中
5.調(diào)用Thread類(lèi)的start方法:開(kāi)啟線(xiàn)程牧抵,調(diào)用Runnable子類(lèi)的run方法
-
好處
1.避免了單繼承的局限性
2.多個(gè)線(xiàn)程可以共享同一個(gè)接口實(shí)現(xiàn)類(lèi)的對(duì)象,非常適合多個(gè)相同的線(xiàn)程來(lái)處理同一份資源
線(xiàn)程的調(diào)度
調(diào)度策略
時(shí)間片
搶占式:高優(yōu)先級(jí)的線(xiàn)程搶占CPU
Java的調(diào)度方法
同優(yōu)先級(jí)線(xiàn)程組成先進(jìn)先出隊(duì)列(先到先服務(wù))侨把,使用時(shí)間片策略
對(duì)高優(yōu)先級(jí)犀变,使用優(yōu)先調(diào)度的搶占式策略
線(xiàn)程的優(yōu)先級(jí)
線(xiàn)程的優(yōu)先級(jí)等級(jí)
MAX_PRIORITY :10
MIN _PRIORITY :1
NORM_PRIORITY :5
涉及方法
getPriority() :返回線(xiàn)程優(yōu)先值
setPriority(int newPriority) : 改變線(xiàn)程的優(yōu)先級(jí)
說(shuō)明
線(xiàn)程創(chuàng)建時(shí)繼承父線(xiàn)程的優(yōu)先級(jí)
低優(yōu)先級(jí)只是獲得調(diào)度的概率低,并非一定是在高優(yōu)先級(jí)線(xiàn)程之后才被調(diào)用
線(xiàn)程分類(lèi)
Java中的線(xiàn)程分為兩類(lèi):一種是 守護(hù)線(xiàn)程秋柄,一種是 用戶(hù)線(xiàn)程
守護(hù)線(xiàn)程是用來(lái)服務(wù)用戶(hù)線(xiàn)程的获枝,通過(guò)在start()方法前調(diào)用 thread.setDaemon(true)可以把一個(gè)用戶(hù)線(xiàn)程變成一個(gè)守護(hù)線(xiàn)程
Java垃圾回收就是一個(gè)典型的守護(hù)線(xiàn)程
它們?cè)趲缀趺總€(gè)方面都是相同的,唯一的區(qū)別是判斷JVM何時(shí)離開(kāi)
若JVM中都是守護(hù)線(xiàn)程骇笔,當(dāng)前JVM將退出
線(xiàn)程的生命周期
概述
JDK 中用Thread.State 類(lèi)定義了 線(xiàn)程的幾種狀態(tài)
新建: 當(dāng)一個(gè)Thread類(lèi)或其子類(lèi)的對(duì)象被聲明并創(chuàng)建時(shí)省店,新生的線(xiàn)程對(duì)象處于新建 狀態(tài)
就緒:處于新建狀態(tài)的線(xiàn)程被start()后,將進(jìn)入線(xiàn)程隊(duì)列等待CPU時(shí)間片蜘拉,此時(shí)它已 具備了運(yùn)行的條件萨西,只是沒(méi)分配到CPU資源
運(yùn)行:當(dāng)就緒的線(xiàn)程被調(diào)度并獲得CPU資源時(shí),便進(jìn)入運(yùn)行狀態(tài), run()方法定義了線(xiàn)程的操作和功能
阻塞:在某種特殊情況下旭旭,被人為掛起或執(zhí)行輸入輸出操作時(shí)谎脯,讓出 CPU 并臨時(shí)中止自己的執(zhí)行,進(jìn)入阻塞狀態(tài)
死亡:線(xiàn)程完成了它的全部工作或線(xiàn)程被提前強(qiáng)制性地中止或出現(xiàn)異常導(dǎo)致結(jié)束
看圖
線(xiàn)程的同步
出現(xiàn)的安全問(wèn)題
當(dāng)多條語(yǔ)句在操作同一個(gè)線(xiàn)程共享數(shù)據(jù)時(shí),一個(gè)線(xiàn)程對(duì)多條語(yǔ)句只執(zhí)行了一部分,還沒(méi)有執(zhí)行完吉挣,另一個(gè)線(xiàn)程參與進(jìn)來(lái)執(zhí)行。導(dǎo)致共享數(shù)據(jù)的錯(cuò)誤
解決方案:對(duì)多條操作共享數(shù)據(jù)的語(yǔ)句废麻,只能讓一個(gè)線(xiàn)程都執(zhí)行完,在執(zhí)行過(guò)程中模庐,其他線(xiàn)程不可以參與執(zhí)行
同步機(jī)制 Synchronized
Java 對(duì)于多線(xiàn)程的安全問(wèn)題提供了專(zhuān)業(yè)的解決 方式 : 同步機(jī)制
用法:
//同步代碼塊 :
synchronized ( 對(duì)象){
// 需要被同步的代碼烛愧;
}
// synchronized 還可以放在方法聲明中,表示整個(gè)方法為同步方法
public synchronized void show (String name){
….
}
Synchronized鎖
在《Thinking in Java》中掂碱,是這么說(shuō)的:對(duì)于并發(fā)工作怜姿,你需要某種方式來(lái)防
止兩個(gè)任務(wù)訪(fǎng)問(wèn)相同的資源(其實(shí)就是共享資源競(jìng)爭(zhēng))。 防止這種沖突的方法
就是當(dāng)資源被一個(gè)任務(wù)使用時(shí)疼燥,在其上加鎖沧卢。第一個(gè)訪(fǎng)問(wèn)某項(xiàng)資源的任務(wù)必須
鎖定這項(xiàng)資源,使其他任務(wù)在其被解鎖之前醉者,就無(wú)法訪(fǎng)問(wèn)它了但狭,而在其被解鎖
之時(shí)披诗,另一個(gè)任務(wù)就可以鎖定并使用它了
-
哪些操作會(huì)釋放鎖?
1.當(dāng)前線(xiàn)程的同步方法立磁、同步代碼塊執(zhí)行結(jié)束
2.當(dāng)前線(xiàn)程在同步代碼塊呈队、同步方法中遇到break、return終止了該代碼塊息罗、 該方法的繼續(xù)執(zhí)行
3.當(dāng)前線(xiàn)程在同步代碼塊掂咒、同步方法中出現(xiàn)了未處理的Error或Exception,導(dǎo)致異常結(jié)束
4.當(dāng)前線(xiàn)程在同步代碼塊迈喉、同步方法中執(zhí)行了線(xiàn)程對(duì)象的wait()方法,當(dāng)前線(xiàn)程暫停温圆,并釋放鎖
-
哪些操作不會(huì)釋放鎖挨摸?
1.線(xiàn)程執(zhí)行同步代碼塊或同步方法時(shí),程序調(diào)用Thread.sleep()岁歉、 Thread. Yield()方法暫停當(dāng)前線(xiàn)程的執(zhí)行
2.線(xiàn)程執(zhí)行同步代碼塊時(shí)得运,其他線(xiàn)程調(diào)用了該線(xiàn)程的suspend()方法將該線(xiàn)程掛起,該線(xiàn)程不會(huì)釋放鎖(同步監(jiān)視器)锅移,應(yīng)盡量避免使用suspend()和resume()來(lái)控制線(xiàn)程
死鎖
- 含義:不同的線(xiàn)程分別占用對(duì)方需要的同步資源不放棄熔掺,都在等待對(duì)方放棄自己需要的同步資源,就形成了線(xiàn)程的死鎖
Lock(鎖)
- 含義: 從JDK 5.0開(kāi)始非剃,Java提供了更強(qiáng)大的線(xiàn)程同步機(jī)制——通過(guò)顯式定義同步鎖對(duì)象來(lái)實(shí)現(xiàn)同步置逻。同步鎖使用Lock對(duì)象充當(dāng)
java.util.concurrent.locks.Lock接口是控制多個(gè)線(xiàn)程對(duì)共享資源進(jìn)行訪(fǎng)問(wèn)的
工具。鎖提供了對(duì)共享資源的獨(dú)占訪(fǎng)問(wèn)备绽,每次只能有一個(gè)線(xiàn)程對(duì)Lock對(duì)象
加鎖券坞,線(xiàn)程開(kāi)始訪(fǎng)問(wèn)共享資源之前應(yīng)先獲得Lock對(duì)象
?
ReentrantLock 類(lèi)實(shí)現(xiàn)了 Lock ,它擁有與 synchronized 相同的并發(fā)性和
內(nèi)存語(yǔ)義肺素,在實(shí)現(xiàn)線(xiàn)程安全的控制中恨锚,比較常用的是ReentrantLock,可以
顯式加鎖倍靡、釋放鎖
- 代碼
class A{
private final ReentrantLock lock = new ReenTrantLock();
public void m(){
lock.lock();
try{
// 保證線(xiàn)程安全的代碼;
}finally{
lock.unlock();
}
}
}
synchronized 與 Lock 的對(duì)比
1.Lock是顯式鎖(手動(dòng)開(kāi)啟和關(guān)閉鎖猴伶,別忘記關(guān)閉鎖),synchronized是 隱式鎖塌西,出了作用域自動(dòng)釋放
2.Lock只有代碼塊鎖他挎,synchronized有代碼塊鎖和方法鎖
3.使用Lock鎖,JVM將花費(fèi)較少的時(shí)間來(lái)調(diào)度線(xiàn)程雨让,性能更好雇盖。并且具有 更好的擴(kuò)展性(提供更多的子類(lèi))
進(jìn)程的通信
wait() 與 notify() 和 notifyAll()
wait():令當(dāng)前線(xiàn)程掛起并放棄CPU、同步資源并等待栖忠,使別的線(xiàn)程可訪(fǎng)問(wèn)并修改共享資源崔挖,而當(dāng)前線(xiàn)程排隊(duì)等候其他線(xiàn)程調(diào)用notify()或notifyAll()方法喚醒贸街,喚醒后等待重新獲得對(duì)監(jiān)視器的所有權(quán)后才能繼續(xù)執(zhí)行
notify(): 喚醒正在排隊(duì)等待同步資源的線(xiàn)程中優(yōu)先級(jí)最高者結(jié)束等待
notifyAll ():?jiǎn)拘颜谂抨?duì)等待資源的所有線(xiàn)程結(jié)束等待
說(shuō)明:這三個(gè)方法只有在synchronized方法或synchronized代碼塊中才能使用,否則會(huì)報(bào)
java.lang.IllegalMonitorStateException
異常