1. 進程和線程
- 進程是系統(tǒng)進行資源分配的基本單位。早期年柠,進程是程序的基本執(zhí)行實體凿歼,當代計算機結構中,進程是線程的容器冗恨。
-
線程是輕量級的進程答憔,是程序執(zhí)行的最小單位。使用多線程而不是多進程是因為線程的切換和調度的成本遠遠小于進程掀抹。
線程生命周期
- NEW : 剛剛創(chuàng)建的線程攀唯,還沒開始執(zhí)行,等到start()方法調用渴丸,獲得相關資源后進入RUNNABLE狀態(tài)。
- RUNNABLE:線程執(zhí)行狀態(tài)。
- BLOCKED:線程執(zhí)行時遇到sychronized同步塊谱轨,進入BLOCKED狀態(tài)戒幔。線程暫停執(zhí)行,直到獲取請求的鎖土童,進入RUNNABLE诗茎。
- WAITING和TIMED_WAITING:等待狀態(tài),等待一些事件献汗。如:wait()方法等待的線程在等待notify()方法敢订,join()方法等待的線程在等待目標線程停止。等到期望事件后進入RUNNABLE狀態(tài)罢吃。
- TERMINATED:線程執(zhí)行完畢楚午。
2. 初始線程:線程的基本操作
2.1 新建線程
- 繼承Thread類,重寫run方法(Java單繼承尿招,不建議使用)
- 實現(xiàn)Runnable接口矾柜,實現(xiàn)run方法,將實現(xiàn)類對象傳入Thread構造函數(shù)就谜。
class Thread1 implements Runnable{
@Override
public void run() {
System.out.println("i am a thread");
}
}
Thread thread2 = new Thread(new Thread1());
thread.start();
2.2 終止線程
- Thread提供了一個stop()方法怪蔑,但是不建議使用,這個方法會直接終止線程丧荐,釋放這個線程所持有的所有鎖缆瓣。會破壞數(shù)據(jù)一致性。例如寫線程正在寫數(shù)據(jù)虹统,直接stop弓坞,讀線程獲取鎖,此時讀出的數(shù)據(jù)是不對的窟却。
- 要想終止線程昼丑,可以設置一個flag標志,需要終止時夸赫,調用方法菩帝,改變flag的值。
2.3 中斷線程
- 中斷線程不會使線程立即退出茬腿,而是給線程發(fā)送一個通知呼奢,告知目標線程,至于目標線程如何處理切平,完全由目標線程自行決定握础。
void interrupt():中斷線程
boolean isInterrupted():判斷是否被中斷
static boolean interrupted():判斷是否被中斷,并且清除中斷標志
Thread t1 = new Thread() {
public void run() {
while(true) {
if(Thread.currentThread().isInterrupted()) {
System.out.println("準備退出");
break;
}
try {
Thread.sleep(2000);
}catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
Thread.yield();
}
}
};
t1.start();
Thread.sleep(2000);
t1.interrupt();
- 中斷sleep()的線程會拋出InterruptedException異常悴品,并且清除中斷標志禀综,如果不處理简烘,下次循環(huán)不會檢測到已中斷,所以在異常處理中再次中斷自己定枷。
2.4 等待(wait)和通知(notify)
- 兩個方法屬于Object類
- 一個對象obj調用wait()方法后孤澎,當前線程就會停止執(zhí)行,進入obj的等待隊列欠窒,直到obj調用notify或者notifyAll方法覆旭。調用notify時,隨機從obj的等待隊列中選擇一個線程喚醒岖妄。
- 調用wait和notify之前型将,都需要獲取此對象obj的鎖。
- wait()方法會釋放所有的鎖荐虐,sleep()方法不會釋放七兜。
等待隊列
wait和notify工作流程
2.5 掛起(suspend)和繼續(xù)執(zhí)行(resume)
- 已廢棄
- 被suspend的線程只有等到resume后才能繼續(xù)執(zhí)行。
- suspend在暫停線程的同時缚俏,不會釋放任何資源惊搏。
- 被suspend的線程狀態(tài)還是RUNNABLE。
2.6 等待線程結束(join)和謙讓(yield)
- join:當前線程阻塞忧换,直到目標線程執(zhí)行完畢恬惯。
- yield:當前線程讓出CPU,重新等待調度亚茬。
3. 分門別類的管理:線程組
- 給線程分組
ThreadGroup tg = new ThreadGroup("A組");
Thread t1 = new Thread(tg,new Thread(),"線程1");
Thread t2 = new Thread(tg,new Thread(),"線程2");
System.out.println(tg.activeCount());
tg.list();
4 守護線程(Daemon)
- 作為系統(tǒng)后臺服務酪耳。
- 其他非守護線程結束后,守護線程自動結束刹缝。
-setDaemon()必須在start()之前設置碗暗。
Thread t1 = new Thread();
t1.setDaemon(true);
5. 線程優(yōu)先級
- 優(yōu)先級高的線程在競爭資源時會更有優(yōu)勢,但只是概率問題梢夯。
- 優(yōu)先級與底層操作系統(tǒng)密切聯(lián)系言疗,各個平臺表現(xiàn)不一。
//Java中用1-10表示颂砸,值越大優(yōu)先級越高
public final static int MIN_PRIORITY = 1;
public final static int NORM_PRIORITY = 5;
public final static int MAX_PRIORITY = 10;
6. 線程安全與synchronized
- volatile一定程度上改善了線程安全的問題噪奄,但是只能保證一個線程修改了數(shù)據(jù)后,其他線程能夠看到這個改動人乓。當兩個線程同時修改這個數(shù)據(jù)時勤篮,依然會產生沖突。
- sychronized可以實現(xiàn)線程的同步色罚,通過對代碼加鎖碰缔,使得每一次只能有一個線程進入同步塊。
- 編碼時要確保是同一個鎖戳护。
- 三種加鎖方式
- 指定加鎖對象金抡。
- 作用于實例方法:相當于對當前實例加鎖瀑焦。
- 作用于靜態(tài)方法:相當于對當前類加鎖。
7. 詭異的錯誤
- 并發(fā)下普通的集合是不安全的梗肝。
- 錯誤的加鎖方式
//Integer是不可變的對象蝠猬,一旦創(chuàng)建就不能修改,實際上每次i++都新建了Integer對象重新賦值统捶,所以每次的鎖不是同一個。
Integer i = 0;
sychronized(i){
i++;
}