線程狀態(tài)
-
新建狀態(tài)
new出一個線程,線程還沒有開始運行,當線程處于新建狀態(tài)時,程序還沒有運行線程中的代碼。
-
就緒狀態(tài)
當線程對象調(diào)用start()方法即啟動了線程,start()方法創(chuàng)建線程運行的系統(tǒng)資源,并調(diào)度線程運行run()方法。當start()方法返回后,線程就處于就緒狀態(tài)。處于就緒狀態(tài)的線程并不一定立即運行run()方法,線程還必須同其他線程競爭CPU時間,只有獲得CPU時間才可以運行線程。對處于就緒狀態(tài)的線程是由Java運行時系統(tǒng)的線程調(diào)度程序(thread scheduler)來調(diào)度的。
-
運行狀態(tài)
當線程獲得CPU時間后,它才進入運行狀態(tài),真正開始執(zhí)行run()方法。
-
阻塞狀態(tài)
當一個線程試圖獲取一個內(nèi)部的對象鎖(非java.util.concurrent庫中的鎖),而該鎖被其他線程持有,則該線程進入阻塞狀態(tài)。
等待狀態(tài)
處于等待狀態(tài)的線程正在等待另一個線程執(zhí)行特定操作,例如Object.wait()等待一個線程notify,Thread.join()等待另一個線程執(zhí)行結(jié)束。-
死亡狀態(tài)
有兩個原因會導(dǎo)致線程死亡:
(1)run方法正常退出而自然死亡,
(2)一個未捕獲的異常終止了run方法而使線程猝死。
線程調(diào)度
線程休眠
線程睡眠是線程主動讓出CPU讓其他線程執(zhí)行,線程從運行狀態(tài)切換為阻塞狀態(tài),線程休眠是其他線程執(zhí)行的有效方法,但是休眠時間到休眠線程未必執(zhí)行,它的執(zhí)行依靠CPU的調(diào)度。但是線程在休眠的時候仍然占有鎖,而且sleep()是靜態(tài)方法,只能控制當前正在運行的線程。
/**
* 睡眠
* 一個計數(shù)器,計數(shù)到 100,在每個數(shù)字之間暫停 1 秒,每隔 10 個數(shù)字輸出一個字符串
*/
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();
}
}
線程讓步
運行狀態(tài)的線程讓出CPU資源,但是然給誰不知道,僅僅是讓出,也有可能是自己,線程從運行狀態(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)用場景是當一個線程必須等待另一個線程執(zhí)行完畢才能執(zhí)行時可以使用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í)行!
線程守候
守護線程在沒有用戶線程可服務(wù)時自動離開,在Java虛擬機中當正在運行的線程都是守護線程時,Java虛擬機退出。調(diào)用線程對象的方法setDaemon(true),則可以將其設(shè)置為守護線程,該方法必須在線程調(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)先級越高。在一個線程中開啟另外一個新線程,則新開線程稱為該線程的子線程,子線程初始優(yōu)先級與父線程相同。
注意:線程的優(yōu)先級仍然無法保障線程的執(zhí)行次序。只不過,優(yōu)先級高的線程獲取CPU資源的概率較大,優(yōu)先級低的并非沒機會執(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是阻塞。
代碼驗證:
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的源碼你會發(fā)現(xiàn),其實ReentrantLock是AQS實現(xiàn)的,而AQS中使用了park()和unpark().