線程狀態(tài)
-
新建狀態(tài)
new出一個(gè)線程伞芹,線程還沒有開始運(yùn)行忘苛,當(dāng)線程處于新建狀態(tài)時(shí)蝉娜,程序還沒有運(yùn)行線程中的代碼。
-
就緒狀態(tài)
當(dāng)線程對象調(diào)用start()方法即啟動(dòng)了線程扎唾,start()方法創(chuàng)建線程運(yùn)行的系統(tǒng)資源召川,并調(diào)度線程運(yùn)行run()方法。當(dāng)start()方法返回后胸遇,線程就處于就緒狀態(tài)荧呐。處于就緒狀態(tài)的線程并不一定立即運(yùn)行run()方法,線程還必須同其他線程競爭CPU時(shí)間纸镊,只有獲得CPU時(shí)間才可以運(yùn)行線程倍阐。對處于就緒狀態(tài)的線程是由Java運(yùn)行時(shí)系統(tǒng)的線程調(diào)度程序(thread scheduler)來調(diào)度的。
-
運(yùn)行狀態(tài)
當(dāng)線程獲得CPU時(shí)間后逗威,它才進(jìn)入運(yùn)行狀態(tài)峰搪,真正開始執(zhí)行run()方法。
-
阻塞狀態(tài)
當(dāng)一個(gè)線程試圖獲取一個(gè)內(nèi)部的對象鎖(非java.util.concurrent庫中的鎖)凯旭,而該鎖被其他線程持有概耻,則該線程進(jìn)入阻塞狀態(tài)。
等待狀態(tài)
處于等待狀態(tài)的線程正在等待另一個(gè)線程執(zhí)行特定操作罐呼,例如Object.wait()等待一個(gè)線程notify鞠柄,Thread.join()等待另一個(gè)線程執(zhí)行結(jié)束。-
死亡狀態(tài)
有兩個(gè)原因會(huì)導(dǎo)致線程死亡:
(1)run方法正常退出而自然死亡嫉柴,
(2)一個(gè)未捕獲的異常終止了run方法而使線程猝死厌杜。
線程調(diào)度
線程休眠
線程睡眠是線程主動(dòng)讓出CPU讓其他線程執(zhí)行,線程從運(yùn)行狀態(tài)切換為阻塞狀態(tài)差凹,線程休眠是其他線程執(zhí)行的有效方法期奔,但是休眠時(shí)間到休眠線程未必執(zhí)行侧馅,它的執(zhí)行依靠CPU的調(diào)度危尿。但是線程在休眠的時(shí)候仍然占有鎖,而且sleep()是靜態(tài)方法馁痴,只能控制當(dāng)前正在運(yùn)行的線程谊娇。
/**
* 睡眠
* 一個(gè)計(jì)數(shù)器,計(jì)數(shù)到 100,在每個(gè)數(shù)字之間暫停 1 秒,每隔 10 個(gè)數(shù)字輸出一個(gè)字符串
*/
public class ThreadSleep {
public static void main(String[] args) {
new Thread(new Runnable() {
public void run() {
for(int i = 0; i < 100; i++){
if((i) % 10 ==0 ){
System.out.println("************");
}
try {
Thread.sleep(1);
System.out.println("i的值是:" + i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
}
線程讓步
運(yùn)行狀態(tài)的線程讓出CPU資源,但是然給誰不知道罗晕,僅僅是讓出济欢,也有可能是自己,線程從運(yùn)行狀態(tài)切換為阻塞狀態(tài)小渊。
public class ThreadYield {
public static void main(String[] args) {
Thread t1 = new MyThread1();
Thread t2 = new Thread(new MyRunnable());
t2.start();
t1.start();
}
static class MyThread1 extends Thread {
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("線程1第" + i + "次執(zhí)行法褥!");
}
}
}
static class MyRunnable implements Runnable {
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("線程2第" + i + "次執(zhí)行!");
Thread.yield();
}
}
}
}
//執(zhí)行結(jié)果(可以看出線程線程1執(zhí)行完后線程1開始執(zhí)行)
線程2第0次執(zhí)行酬屉!
線程1第0次執(zhí)行半等!
線程1第1次執(zhí)行揍愁!
線程1第2次執(zhí)行!
線程1第3次執(zhí)行杀饵!
線程1第4次執(zhí)行莽囤!
線程1第5次執(zhí)行!
線程1第6次執(zhí)行切距!
線程1第7次執(zhí)行朽缎!
線程1第8次執(zhí)行!
線程1第9次執(zhí)行谜悟!
線程2第1次執(zhí)行话肖!
線程2第2次執(zhí)行!
線程2第3次執(zhí)行赌躺!
線程2第4次執(zhí)行狼牺!
線程2第5次執(zhí)行!
線程2第6次執(zhí)行礼患!
線程2第7次執(zhí)行是钥!
線程2第8次執(zhí)行!
線程2第9次執(zhí)行缅叠!
線程合并
現(xiàn)在有線程A悄泥、線程B,線程合并是將線程A肤粱、B合二為一弹囚,線程A先執(zhí)行,然后調(diào)用join方法领曼,則線程B開始執(zhí)行鸥鹉,等B執(zhí)行完成后A再接著上次繼續(xù)執(zhí)行。應(yīng)用場景是當(dāng)一個(gè)線程必須等待另一個(gè)線程執(zhí)行完畢才能執(zhí)行時(shí)可以使用join方法庶骄。
public class ThreadJoin extends Thread {
public static void main(String[] args) {
Thread t1 = new MyThread2();
t1.start();
for (int i = 0; i < 10; i++) {
System.out.println("主線程第" + i + "次執(zhí)行!");
if (i > 2) try {
//t1 線程合并到主線程中,主線程停止執(zhí)行過程,轉(zhuǎn)而執(zhí)行 t1 線程, 直到 t1 執(zhí)行完畢后繼續(xù)毁渗。
t1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class MyThread2 extends Thread {
public void run() {
for (int i = 0; i < 4; i++) {
System.out.println("線程 1 第" + i + "次執(zhí)行!");
}
}
}
//執(zhí)行結(jié)果
主線程第0次執(zhí)行!
主線程第1次執(zhí)行!
主線程第2次執(zhí)行!
主線程第3次執(zhí)行!
線程 1 第0次執(zhí)行!
線程 1 第1次執(zhí)行!
線程 1 第2次執(zhí)行!
線程 1 第3次執(zhí)行!
主線程第4次執(zhí)行!
主線程第5次執(zhí)行!
主線程第6次執(zhí)行!
主線程第7次執(zhí)行!
主線程第8次執(zhí)行!
主線程第9次執(zhí)行!
線程守候
守護(hù)線程在沒有用戶線程可服務(wù)時(shí)自動(dòng)離開,在Java虛擬機(jī)中當(dāng)正在運(yùn)行的線程都是守護(hù)線程時(shí)单刁,Java虛擬機(jī)退出灸异。調(diào)用線程對象的方法setDaemon(true),則可以將其設(shè)置為守護(hù)線程羔飞,該方法必須在線程調(diào)用前執(zhí)行肺樟。
public class ThreadDaemon {
public static void main(String[] args) {
Thread t1 = new MyCommon();
Thread t2 = new MyDaemon();
t2.setDaemon(true);
t1.start();
t2.start();
}
}
class MyCommon extends Thread {
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println("線程MyCommon第" + i + "次執(zhí)行!");
try {
Thread.sleep(7);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class MyDaemon extends Thread {
public void run() {
for (int i = 0; i < 400; i++) {
System.out.println("線程 MyDaemon 第" + i + "次執(zhí)行!");
try {
Thread.sleep(7);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//執(zhí)行結(jié)果(MyDaemon線程還沒有執(zhí)行完成便退出)
線程MyCommon第0次執(zhí)行!
線程 MyDaemon 第0次執(zhí)行!
線程 MyDaemon 第1次執(zhí)行!
線程MyCommon第1次執(zhí)行!
線程MyCommon第2次執(zhí)行!
線程 MyDaemon 第2次執(zhí)行!
線程MyCommon第3次執(zhí)行!
線程 MyDaemon 第3次執(zhí)行!
線程MyCommon第4次執(zhí)行!
線程 MyDaemon 第4次執(zhí)行!
線程 MyDaemon 第5次執(zhí)行!
線程優(yōu)先級
線程的有限執(zhí)行順序,優(yōu)先級1~10逻淌,數(shù)值越大優(yōu)先級越高么伯。在一個(gè)線程中開啟另外一個(gè)新線程,則新開線程稱為該線程的子線程卡儒,子線程初始優(yōu)先級與父線程相同田柔。
注意:線程的優(yōu)先級仍然無法保障線程的執(zhí)行次序誓篱。只不過,優(yōu)先級高的線程獲取CPU資源的概率較大凯楔,優(yōu)先級低的并非沒機(jī)會(huì)執(zhí)行窜骄。
public class ThreadPriority {
public static void main(String[] args) {
Thread threadA = new Thread(new ThreadA());
Thread threadB = new Thread(new ThreadB());
threadA.setPriority(10);
threadB.setPriority(1);
threadB.start();
threadA.start();
}
}
class ThreadA extends Thread {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(" A線程第" + i + "次執(zhí)行");
}
}
}
class ThreadB extends Thread {
@Override
public void run() {
for (int j = 0; j < 10; j++) {
System.out.println("B線程第" + j + "次執(zhí)行");
}
}
}
//執(zhí)行結(jié)果
B線程第0次執(zhí)行
B線程第1次執(zhí)行
A線程第0次執(zhí)行
B線程第2次執(zhí)行
A線程第1次執(zhí)行
A線程第2次執(zhí)行
B線程第3次執(zhí)行
B線程第4次執(zhí)行
B線程第5次執(zhí)行
B線程第6次執(zhí)行
B線程第7次執(zhí)行
B線程第8次執(zhí)行
B線程第9次執(zhí)行
A線程第3次執(zhí)行
問題
- 為什么synchronized等到獲取鎖是阻塞,而ReentrantLock是阻塞摆屯。
代碼驗(yàn)證:
public class StatusDemo {
static Object lock = new Object();
public static void read() {
synchronized (lock) {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("讀數(shù)據(jù)");
}
}
public static void write() {
synchronized (lock) {
System.out.println("寫數(shù)據(jù)");
}
}
public static void main(String[] args) throws InterruptedException {
new Thread(new Runnable() {
@Override
public void run() {
StatusDemo.read();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
StatusDemo.write();
}
}).start();
}
}
public class StatusDemo {
static ReentrantLock lock = new ReentrantLock();
public static void read() {
lock.lock();
try {
System.out.println(Thread.currentThread().getName()+"讀數(shù)據(jù)");
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public static void write() {
lock.lock();
try {
System.out.println(Thread.currentThread().getName()+"寫數(shù)據(jù)");
}finally {
lock.unlock();
}
}
public static void main(String[] args) throws InterruptedException {
new Thread(new Runnable() {
@Override
public void run() {
StatusDemo1.read();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
StatusDemo1.write();
}
}).start();
}
}
為什么ReentrantLock是阻塞呢邻遏,看ReentrantLock的源碼你會(huì)發(fā)現(xiàn),其實(shí)ReentrantLock是AQS實(shí)現(xiàn)的虐骑,而AQS中使用了park()和unpark().