線程生命周期
線程狀態(tài):
- 新建狀態(tài):使用new關(guān)鍵字和Thread類或其子類建立一個(gè)線程對(duì)象后
- 就緒狀態(tài):當(dāng)線程對(duì)象調(diào)用了start()方法之后,該線程進(jìn)入就緒狀態(tài)
- 運(yùn)行狀態(tài):如果就緒狀態(tài)的線程獲取CPU資源掉房,就可以執(zhí)行run()
- 阻塞狀態(tài):
- 等待阻塞:運(yùn)行狀態(tài)中的線程執(zhí)行wait()方法
- 同步阻塞:線程在獲取 synchronized 同步鎖失敗
- 其他阻塞:通過調(diào)用線程的sleep()或join()發(fā)出I/O請(qǐng)求時(shí)
- 死亡狀態(tài):一個(gè)運(yùn)行狀態(tài)的線程完成人文或者其他終止條件發(fā)送
創(chuàng)建線程
1终息、繼承Thread類
class ThreadDemo extends Thread
2煤率、實(shí)現(xiàn)Runable接口
public class RunTest implements Runnable
Thread thread1 = new Thread(new RunTest());
3、通過Callable、Future稿壁、FutureTask創(chuàng)建線程
// 繼承實(shí)現(xiàn)Callable接口,聲明返回類型
class CallTest implements Callable<Long>
FutureTask<Long> futureTask = new FutureTask<Long>(new CallTest(searchVo));
線程內(nèi)置方法
1奥秆、sleep:使當(dāng)前線程(即調(diào)用該方法的線程)暫停執(zhí)行一段時(shí)間逊彭,讓其它線程有機(jī)會(huì)繼續(xù)執(zhí)行,但它不會(huì)釋放對(duì)象鎖构订。
2侮叮、yield:暫停當(dāng)前正在執(zhí)行的線程對(duì)象,并執(zhí)行其他線程悼瘾。yield的目的是讓相同優(yōu)先級(jí)的線程之間能適當(dāng)?shù)?strong>輪轉(zhuǎn)執(zhí)行
3囊榜、join:把指定的線程加入到當(dāng)前線程,可以將兩個(gè)交替執(zhí)行的線程合并為順序執(zhí)行的線程亥宿。比如線程B中調(diào)用了線程A的JOIN()方法卸勺,直到線程A執(zhí)行完畢后,才會(huì)繼續(xù)執(zhí)行線程B烫扼。
//主線程等待子線程thread執(zhí)行結(jié)束才會(huì)輸出結(jié)果
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new JoinTest());
thread.start();
thread.join(); //加入join()
System.out.println("主線程結(jié)束");
}
4曙求、setDaemon:
Java中線程分為兩種類型:用戶線程和守護(hù)線程。通過Thread.setDaemon(false)設(shè)置為用戶線程映企;通過Thread.setDaemon(true)設(shè)置為守護(hù)線程悟狱。如果不設(shè)置次屬性,默認(rèn)為用戶線程卑吭。
public class DaemonTest extends Thread {
public void run() { //永真循環(huán)線程
for (int i = 0; ; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
}
System.out.println(i);
}
}
public static void main(String[] args) {
Thread daemonTest = new DaemonTest();
daemonTest.setDaemon(true); //調(diào)試時(shí)可以設(shè)置為false芽淡,那么這個(gè)程序是個(gè)死循環(huán),沒有退出條件豆赏。設(shè)置為true挣菲,即可主線程結(jié)束,test線程也結(jié)束掷邦。
daemonTest.start();
System.out.println("isDaemon = " + daemonTest.isDaemon());
try {
System.in.read(); // 接受輸入白胀,使程序在此停頓,一旦接收到用戶輸入抚岗,main線程結(jié)束或杠,守護(hù)線程自動(dòng)結(jié)束
} catch (IOException ex) {
}
}
}
如果線程設(shè)置為Thread.setDaemon(true),則主線程結(jié)束該程序不會(huì)結(jié)束宣蔚,必須等待子線程執(zhí)行結(jié)束整個(gè)程序才能結(jié)束向抢;如果線程設(shè)置為Thread.setDaemon(false),則主線程結(jié)束整個(gè)程序就結(jié)束胚委。
5挟鸠、wait、notify亩冬、notifyAll
當(dāng)一個(gè)線程進(jìn)入wait之后艘希,就必須等待其他線程notify/notifyAll,使用notifyAll可以喚醒所有處于wait狀態(tài)的線程,使其重新進(jìn)入鎖的爭(zhēng)奪隊(duì)列中覆享,而notify只能喚醒一個(gè)佳遂。
notify被喚醒的線程是隨機(jī)的,所以通常是沒辦法指定是誰被喚醒撒顿。
線程關(guān)鍵字
- volatile
可見性:對(duì)一個(gè)volatile變量的讀丑罪,總是能看到(任意線程)對(duì)這個(gè)volatile變量最后的寫入
原子性:對(duì)任意單個(gè)volatile變量的讀/寫具有原子性,但是類似于volatile++這種復(fù)合操作不具有原子性 - synchronized
當(dāng)它用來修飾一個(gè)方法或者一個(gè)代碼塊的時(shí)候核蘸,能夠保證在同一時(shí)刻最多只有一個(gè)線程執(zhí)行該段代碼巍糯。然而,當(dāng)一個(gè)線程訪問object的一個(gè)synchronized(this)同步代碼塊時(shí)客扎,另一個(gè)線程仍然可以訪問該object中的非synchronized(this)同步代碼塊。
synchronized 關(guān)鍵字罚斗,它包括兩種用法:synchronized 方法和 synchronized 塊徙鱼。
synchronized 方法:通過在方法聲明中加入 synchronized關(guān)鍵字來聲明 synchronized 方法
public synchronized void accessVal(int newVal);
synchronized 塊:通過 synchronized關(guān)鍵字來聲明synchronized 塊
synchronized(syncObject) {
//允許訪問控制的代碼
}
ThreadLocal
ThreadLocal是一個(gè)關(guān)于創(chuàng)建線程局部變量的類。通常情況下针姿,我們創(chuàng)建的變量是可以被任何一個(gè)線程訪問修改的袱吆。而使用ThreadLocal創(chuàng)建的變量只能被當(dāng)前線程訪問,其他線程無法訪問和修改距淫。
在Java中绞绒,棧內(nèi)存歸屬于單個(gè)線程,每個(gè)線程都會(huì)有一個(gè)棧內(nèi)存榕暇,其存儲(chǔ)的變量只能在其所屬線程中可見蓬衡,即棧內(nèi)存可以理解成線程的私有內(nèi)存。而堆內(nèi)存中的對(duì)象對(duì)所有線程可見彤枢。堆內(nèi)存中的對(duì)象可以被所有線程訪問狰晚。
ThreadLocal并不是存放在棧上。ThreadLocal實(shí)例實(shí)際上也是被其創(chuàng)建的類持有(更頂端應(yīng)該是被線程持有)缴啡。而ThreadLocal的值其實(shí)也是被線程實(shí)例持有壁晒。它們都是位于堆上,只是通過一些技巧將可見性修改成線程可見业栅。
//使用ThreadLocal保存Connection變量
ThreadLocal<Connection> connThreadLocal = new ThreadLocal<Connection>();
//保存到線程本地變量中
connThreadLocal.set(conn);
// 直接返回線程本地變量
connThreadLocal.get();