目錄
1.Thread相關源碼
public class Thread implements Runnable {
// 線程名字
private volatile String name;
// 線程優先級(1~10)
private int priority;
// 守護線程
private boolean daemon = false;
// 線程id
private long tid;
// 線程組
private ThreadGroup group;
// 預定義3個優先級
public final static int MIN_PRIORITY = 1;
public final static int NORM_PRIORITY = 5;
public final static int MAX_PRIORITY = 10;
// 構造函數
public Thread();
public Thread(String name);
public Thread(Runnable target);
public Thread(Runnable target, String name);
// 線程組
public Thread(ThreadGroup group, Runnable target);
// 返回當前正在執行線程對象的引用
public static native Thread currentThread();
// 啓動一個新線程
public synchronized void start();
// 線程的方法體,和啓動線程沒毛關係
public void run();
// 讓線程睡眠一會,由活躍狀態改爲掛起狀態
public static native void sleep(long millis) throws InterruptedException;
public static void sleep(long millis, int nanos) throws InterruptedException;
// 打斷線程 中斷線程 用於停止線程
// 調用該方法時並不需要獲取Thread實例的鎖。無論何時,任何線程都可以調用其它線程的interruptf方法
public void interrupt();
public boolean isInterrupted()
// 線程是否處於活動狀態
public final native boolean isAlive();
// 交出CPU的使用權,從運行狀態改爲掛起狀態
public static native void yield();
public final void join() throws InterruptedException
public final synchronized void join(long millis)
public final synchronized void join(long millis, int nanos) throws InterruptedException
// 設置線程優先級
public final void setPriority(int newPriority);
// 設置是否守護線程
public final void setDaemon(boolean on);
// 線程id
public long getId() { return this.tid; }
// 線程狀態
public enum State {
// new 創建
NEW,
// runnable 就緒
RUNNABLE,
// blocked 阻塞
BLOCKED,
// waiting 等待
WAITING,
// timed_waiting
TIMED_WAITING,
// terminated 結束
TERMINATED;
}
}
public static void main(String[] args) {
// main方法就是一個主線程
// 獲取當前正在運行的線程
Thread thread = Thread.currentThread();
// 線程名字
String name = thread.getName();
// 線程id
long id = thread.getId();
// 線程優先級
int priority = thread.getPriority();
// 是否存活
boolean alive = thread.isAlive();
// 是否守護線程
boolean daemon = thread.isDaemon();
// Thread[name=main, id=1 ,priority=5 ,alive=true ,daemon=false]
System.out.println("Thread[name=" + name + ", id=" + id + " ,priority=" + priority + " ,alive=" + alive + " ,daemon=" + daemon + "]");
}
2.start()與run()
- start(): 啓動一個線程,線程之間是沒有順序的,是按CPU分配的時間片來回切換的。
- run(): 調用線程的run方法,就是普通的方法調用,雖然將代碼封裝到兩個線程體中,可以看到線程中打印的線程名字都是main主線程,run()方法用於封裝線程的代碼,具體要啓動一個線程來運行線程體中的代碼(run()方法)還是通過start()方法來實現,調用run()方法就是一種順序編程不是併發編程。
3. sleep() 與 interrupt()
public static native void sleep(long millis) throws InterruptedException;
public void interrupt();
sleep(long millis): 睡眠指定時間,程序暫停運行,睡眠期間會讓出CPU的執行權,去執行其它線程,同時CPU也會監視睡眠的時間,一旦睡眠時間到就會立刻執行(因爲睡眠過程中仍然保留着鎖,有鎖只要睡眠時間到就能立刻執行)。
- sleep(): 睡眠指定時間,即讓程序暫停指定時間運行,時間到了會繼續執行代碼,如果時間未到就要醒需要使用interrupt()來隨時喚醒;
- interrupt(): 喚醒正在睡眠的程序,調用interrupt()方法,會使得sleep()方法拋出InterruptedException異常,當sleep()方法拋出異常就中斷了sleep的方法,從而讓程序繼續運行下去。
public static void main(String[] args) throws Exception {
Thread thread0 = new Thread(()-> {
try {
System.out.println(new Date() + "\t" + Thread.currentThread().getName() + "\t太困了,讓我睡10秒,中間有事叫我,zZZ。。。");
Thread.sleep(10000);
} catch (InterruptedException e) {
System.out.println(new Date() + "\t" + Thread.currentThread().getName() + "\t被叫醒了,又要繼續幹活了");
}
});
thread0.start();
// 這裏睡眠只是爲了保證先讓上面的那個線程先執行
Thread.sleep(2000);
new Thread(()-> {
System.out.println(new Date() + "\t" + Thread.currentThread().getName() + "\t醒醒,醒醒,別睡了,起來幹活了!!!");
// 無需獲取鎖就可以調用interrupt
thread0.interrupt();
}).start();
}
4.wait() 與 notify()
wait、notify和notifyAll方法是Object類的final native方法。所以這些方法不能被子類重寫,Object類是所有類的超類,因此在程序中可以通過this或者super來調用this.wait(), super.wait()。
- wait(): 導致線程進入等待阻塞狀態,會一直等待直到它被其他線程通過notify()或者notifyAll喚醒。該方法只能在同步方法中調用。如果當前線程不是鎖的持有者,該方法拋出一個IllegalMonitorStateException異常。wait(long timeout): 時間到了自動執行,類似於sleep(long millis)。
- notify(): 該方法只能在同步方法或同步塊內部調用, 隨機選擇一個(注意:只會通知一個)在該對象上調用wait方法的線程,解除其阻塞狀態。
- notifyAll(): 喚醒所有的wait對象。
注意:
- Object.wait()和Object.notify()和Object.notifyall()必須寫在synchronized方法內部或者synchronized塊內部。
- 讓哪個對象等待wait就去通知notify哪個對象,不要讓A對象等待,結果卻去通知B對象,要操作同一個對象
Object類
public class Object {
public final void wait() throws InterruptedException;
public final native void wait(long timeout) throws InterruptedException;
public final void wait(long timeout, int nanos) throws InterruptedException;
public final native void notify();
public final native void notifyAll();
}
測試:
public class WaitNotifyTest {
public static void main(String[] args) throws Exception {
WaitNotifyTest waitNotifyTest = new WaitNotifyTest();
new Thread(() -> {
try {
waitNotifyTest.printFile();
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
new Thread(() -> {
try {
waitNotifyTest.printFile();
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
new Thread(() -> {
try {
System.out.println(new Date() + "\t" + Thread.currentThread().getName() + "\t睡覺1秒中,目的是讓上面的線程先執行,即先執行wait()");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
waitNotifyTest.notifyPrint();
}).start();
}
private synchronized void printFile() throws InterruptedException {
System.out.println(new Date() + "\t" + Thread.currentThread().getName() + "\t等待打印文件...");
this.wait();
System.out.println(new Date() + "\t" + Thread.currentThread().getName() + "\t打印結束。。。");
}
private synchronized void notifyPrint() {
this.notify();
System.out.println(new Date() + "\t" + Thread.currentThread().getName() + "\t通知完成...");
}
}
wait():讓程序暫停執行,相當於讓當前,線程進入當前實例的等待隊列,這個隊列屬於該實例對象,所以調用notify也必須使用該對象來調用,不能使用別的對象來調用。調用wait和notify必須使用同一個對象來調用。
this.notifyAll();
5.sleep() 與 wait()
區別:
- sleep在Thread類中,wait在Object類中
- sleep不會釋放鎖,wait會釋放鎖
- sleep使用interrupt()來喚醒,wait需要notify或者notifyAll來通知
Thread.sleep(long millis): 睡眠時不會釋放鎖。
public static void main(String[] args) throws InterruptedException {
Object lock = new Object();
new Thread(() -> {
synchronized (lock) {
for (int i = 0; i < 5; i++) {
System.out.println(new Date() + "\t" + Thread.currentThread().getName() + "\t" + i);
try { Thread.sleep(1000); } catch (InterruptedException e) { }
}
}
}).start();
Thread.sleep(1000);
new Thread(() -> {
synchronized (lock) {
for (int i = 0; i < 5; i++) {
System.out.println(new Date() + "\t" + Thread.currentThread().getName() + "\t" + i);
}
}
}).start();
}
因main方法中Thread.sleep(1000)所以上面的線程Thread-0先被執行,當循環第一次時就會Thread.sleep(1000)睡眠,因爲sleep並不會釋放鎖,所以Thread-1得不到執行的機會,所以直到Thread-0執行完畢釋放鎖對象lock,Thread-1才能拿到鎖,然後執行Thread-1;
object.wait(long timeout): 會釋放鎖。
public class SleepWaitTest {
public static void main(String[] args) throws InterruptedException {
SleepWaitTest object = new SleepWaitTest();
new Thread(() -> {
synchronized (object) {
System.out.println(new Date() + "\t" + Thread.currentThread().getName() + "\t等待打印文件...");
try {
object.wait(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(new Date() + "\t" + Thread.currentThread().getName() + "\t打印結束。。。");
}
}).start();
// 先上面的線程先執行
Thread.sleep(1000);
new Thread(() -> {
synchronized (object) {
for (int i = 0; i < 5; i++) {
System.out.println(new Date() + "\t" + Thread.currentThread().getName() + "\t" + i);
}
}
}).start();
}
}
因main方法中有Thread.sleep(1000)所以上面的線程Thread-0肯定會被先執行,當Thread-0被執行時就拿到了object對象鎖,然後進入wait(5000)5秒鐘等待,此時wait釋放了鎖,然後Thread-1就拿到了鎖就執行線程體,Thread-1執行完後就釋放了鎖,當等待5秒後Thread-0就能再次獲取object鎖,這樣就繼續執行後面的代碼。wait方法是釋放鎖的,如果wait方法不釋放鎖那麼Thread-1是拿不到鎖也就沒有執行的機會的,事實是Thread-1得到了執行,所以說wait方法會釋放鎖。
6.join()
讓當前線程加入父線程,加入後父線程會一直wait,直到子線程執行完畢後父線程才能執行。當我們調用某個線程的這個方法時,這個方法會掛起調用線程,直到被調用線程結束執行,調用線程纔會繼續執行。
將某個線程加入到當前線程中來,一般某個線程和當前線程依賴關係比較強,必須先等待某個線程執行完畢才能執行當前線程。一般在run()方法內使用。
join()方法:
public final void join() throws InterruptedException {
join(0);
}
public final synchronized void join(long millis) throws InterruptedException {
long base = System.currentTimeMillis();
long now = 0;
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (millis == 0) {
// 循環檢查線程的狀態是否還活着,如果死了就結束了,如果活着繼續等到死
while (isAlive()) {
wait(0);
}
} else {
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
public final synchronized void join(long millis, int nanos) throws InterruptedException {
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (nanos < 0 || nanos > 999999) {
throw new IllegalArgumentException("nanosecond timeout value out of range");
}
if (nanos >= 500000 || (nanos != 0 && millis == 0)) {
millis++;
}
join(millis);
}
join() 和 join(long millis, int nanos) 最後都調用了 join(long millis)。
join(long millis, int nanos)和join(long millis)方法 都是synchronized。
join() 調用了join(0),從源碼可以看到join(0)不斷檢查當前線程是否處於Active狀態。
join() 和 sleep() 一樣,都可以被中斷(被中斷時,會拋出 InterrupptedException 異常);不同的是,join() 內部調用了wait(),會出讓鎖,而 sleep() 會一直保持鎖。
測試代碼:
public class JoinTest {
public static void main(String[] args) {
new Thread(new ParentRunnable()).start();
}
}
class ParentRunnable implements Runnable {
@Override
public void run() {
// 線程處於new狀態
Thread childThread = new Thread(new ChildRunable());
// 線程處於runnable就緒狀態
childThread.start();
try {
// 當調用join時,parent會等待child執行完畢後再繼續運行
// 將某個線程加入到當前線程
childThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < 5; i++) {
System.out.println(new Date() + "\t" + Thread.currentThread().getName() + "父線程 running");
}
}
}
class ChildRunable implements Runnable {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
try { Thread.sleep(1000); } catch (InterruptedException e) {}
System.out.println(new Date() + "\t" + Thread.currentThread().getName() + "子線程 running");
}
}
}
程序進入主線程,運行Parent對應的線程,Parent的線程代碼分兩段,一段是啓動一個子線程,一段是Parent線程的線程體代碼,首先會將Child線程加入到Parent線程,join()方法會調用join(0)方法(join()方法是普通方法並沒有加鎖,join(0)會加鎖),join(0)會執行while(isAlive()) { wait(0);} 循環判斷線程是否處於活動狀態,如果是繼續wait(0)知道isAlive=false結束掉join(0), 從而結束掉join(), 最後回到Parent線程體中繼續執行其它代碼。
在Parent調用child.join()後,child子線程正常運行,Parent父線程會等待child子線程結束後再繼續運行。
---------------------------------------------------end--------------------------------------------------------------
參考博文:https://blog.csdn.net/vbirdbest/article/details/81282163