一、什么是線程
現(xiàn)代操作系統(tǒng)調(diào)度的最小單元是線程,在一個進(jìn)程里可以創(chuàng)建多個線程,這些線程都擁有各自的計(jì)數(shù)器、堆棧和局部變量等屬性,并且能夠訪問共享的內(nèi)存變量。處理器在這些線程上高速切換,讓使用者感覺到這些線程在同時執(zhí)行。
二、為什么要使用多線程
- 更多的處理器核心
一個單線程程序在運(yùn)行時只能使用一個處理器核心,那么再多的處理器核心加入也無法顯著提升該程序的執(zhí)行效率。相反,如果該程序使用多線程技術(shù),將計(jì)算邏輯分配到多個處理器核心上,就會顯著減少程序的處理時間,并且隨著更多處理器核心的加入而變得更有效率。
- 更快的響應(yīng)時間
有時我們會編寫業(yè)務(wù)較為復(fù)雜的代碼。例如,一筆訂單的創(chuàng)建,它包括插入訂單數(shù)據(jù)、生成訂單快照、發(fā)送郵件通知賣家和記錄貨品銷售數(shù)量??梢允褂枚嗑€程技術(shù),將數(shù)據(jù)一致性不強(qiáng)的操作派發(fā)給其他線程處理(也可以使用消息隊(duì)列),如生成訂單快照、發(fā)送郵件等。好處是響應(yīng)用戶請求的線程能夠盡可能快地處理完成,縮短了響應(yīng)時間,提升用戶體驗(yàn)。
三、線程優(yōu)先級
現(xiàn)代操作系統(tǒng)基本采用分時的形式調(diào)度運(yùn)行線程,操作系統(tǒng)會分出一個個時間片,線程會分配到若干時間片,當(dāng)線程的時間片用完了就會發(fā)生線程調(diào)度,等待下次分配。線程優(yōu)先級就是決定線程需要多或者少分配一些處理器資源的線程屬性。
通過一個整型成員變量priority來控制優(yōu)先級,優(yōu)先級范圍從1~10,在線程構(gòu)建時可以通過setPriority(int)方法來修改線程優(yōu)先級,默認(rèn)優(yōu)先級是5。線程優(yōu)先級不能作為程序正確性的依賴,許多操作系統(tǒng)都是完全不理會Java線程對于優(yōu)先級的設(shè)定的。
四、線程的狀態(tài)
| 狀態(tài)名稱 | 說明 |
|---|---|
| NEW | 初始狀態(tài),線程被構(gòu)建,但是還沒有調(diào)用start()方法 |
| RUNNABLE | 運(yùn)行狀態(tài),就緒和 運(yùn)行中兩種狀態(tài)都稱作運(yùn)行中 |
| BLOCKED | 阻塞狀態(tài),標(biāo)識線程阻塞于鎖 |
| WAITING | 等待狀態(tài),表示線程進(jìn)入等待狀態(tài),進(jìn)入該狀態(tài)表示當(dāng)前線程需要等待其他線程做出一些特定動作(通知或中斷) |
| TIME_WAITING | 超時等待狀態(tài),不同于WAITING,它可以在指定時間自行返回 |
| TERMINATED | 終止?fàn)顟B(tài),表示當(dāng)前線程已經(jīng)執(zhí)行完畢 |
// ThreadState.java
public class ThreadState {
public static void main(String[] args) {
new Thread(new TimeWaiting(), "TimeWaitingThread").start();
new Thread(new Waiting(), "WaitingThread").start();
// 使用兩個Blocked線程, 一個獲取鎖成功, 另一個被阻塞
new Thread(new Blocked(), "BlockedThread-1").start();
new Thread(new Blocked(), "BlockedThread-2").start();
}
// 該線程不斷的進(jìn)行睡眠
static class TimeWaiting implements Runnable {
@Override
public void run() {
while (true) {
SleepUtils.second(100);
}
}
}
// 該線程在Waiting.class實(shí)例上等待
static class Waiting implements Runnable {
@Override
public void run() {
while (true) {
synchronized (Waiting.class) {
try {
Waiting.class.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
// 該線程在Blocked.class 實(shí)例上加鎖后,不會釋放該鎖
static class Blocked implements Runnable {
public void run() {
synchronized (Blocked.class) {
while (true) {
SleepUtils.second(100);
}
}
}
}
}
// SleepUtils.java
public class SleepUtils {
public static final void second(long seconds) {
try {
TimeUnit.SECONDS.sleep(seconds);
} catch (InterruptedException e) {
}
}
}
命令行輸入“jps”,得到ThreadState的進(jìn)程id(如929)后,輸入“jstack 929”,嘗試查看示例進(jìn)程運(yùn)行時的線程信息:
// BlockedThread-2 線程阻塞在獲取Blocked.class示例的鎖上
"BlockedThread-2" prio=5 tid=0x00007feacb05d000 nid=0x5d03 waiting for monitor entry
[0x000000010fd58000]
java.lang.Thread.State: BLOCKED (on object monitor)
// BlockedThread-1 線程獲取到了Blocked.class的鎖
"BlockedThread-1" prio=5 tid=0x00007feacb05a000 nid=0x5b03 waiting on condition
[0x000000010fc55000]
java.lang.Thread.State: TIME_WAITING (sleeping)
// WaitingThread線程在Waiting實(shí)例上等待
"WaitingThread" prio=5 tid=0x00007feacb059800 nid=0x5903 in Object.wait()
[0x000000010fb52000]
java.lang.Thread.State: WAITING (on object monitor)
// TimeWaitingThread 線程處于超時等待
"TimeWaitingThread" prio=5 tid=0x00007feacb058800 nid=0x5703 waiting on condition
[0x000000010fa4f000]
java.lang.Thread.State: TIMED_WAITING (sleeping)

五、守護(hù)線程
Daemon線程是一種支持型線程,因?yàn)樗饕挥米鞒绦蛑泻笈_調(diào)度以及支持性工作。這意味著,當(dāng)一個Java虛擬機(jī)中不存在非Daemon線程的時候,Java虛擬機(jī)將會退出。因此,不能依靠Daemon線程的finally塊中的內(nèi)容來確保執(zhí)行關(guān)閉或清理資源的邏輯。
可以通過Thread.setDaemon(true)將線程設(shè)置為Daemon線程。