- 線程 run和start方法說明
run方法:其實是Runnable接口方法,只是個接口規範,業務邏輯都寫在這個方法中(線程結束判斷以及具體業務邏輯)
start方法:Thread 線程類封裝的規範,一個線程實例只有調用了start方法,才能夠開啓新線程執行run。否則只調用run方法,實際並沒有開啓新的線程。start方法只能夠調用一次,多次調用會報出異常 IllegalThreadStateException。start其實並不會開啓線程,內部調用start0()方法纔會真正的開啓線程。
- 線程生命週期
從這個圖中可以看到線程的狀態流轉,這裏主要介紹線程的join和yield方法,後續會詳細講解notify和wait方法。
join 方法(獲取CPU執行權):a線程調用b線程的join方法,就是把當前CPU執行權交給線程b,當執行完b線程後,才執行線程a。一般會用來控制多線程按照順序執行(本來多線程是並行執行的),通過join方法可以控制每個線程的執行順序。
yield方法(奉獻出CPU執行權):調用a線程的yield方法,線程a就會進入就緒狀態,CPU會從就緒線程集合中隨機(有優先級)拿一個線程執行。當然拿到的這個線程也可能還是a線程,yield方法跟鎖沒有任何關係,並不是說釋放鎖。
- join方法案例,多任務按照順序執行
- 無序
import java.util.Random;
public class xu02 {
private static class BuyTicket extends Thread{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"--正在購票--------");
try {
//模擬每個人購票時間不確定
Thread.sleep(new Random().nextInt(50));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"--購票完成--------");
}
}
public static void main(String[] args) {
//創建3個人排隊購票
BuyTicket bird=new BuyTicket();
bird.setName("bird");
BuyTicket dog=new BuyTicket();
dog.setName("dog");
BuyTicket cat=new BuyTicket();
cat.setName("cat");
//1:無序購票(未排隊),這裏並不能夠確定誰先買完票
cat.start();
dog.start();
bird.start();
}
}
- 指定順序購票
package com.hongying.multithread;
import java.util.Random;
public class xu02 {
private static class BuyTicket extends Thread{
private Thread preThread;//排在他前面的人
public BuyTicket(Thread pre){
this.preThread=pre;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"--正在購票--------");
//做個判斷,只有排在前面的人完成購票了才允許購票
if(preThread!=null){
try {
preThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
//模擬每個人購票時間不確定
Thread.sleep(new Random().nextInt(50));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"--購票完成--------");
}
}
public static void main(String[] args) {
//創建3個人排隊購票
BuyTicket cat=new BuyTicket(null);//第一個位置
cat.setName("cat");
BuyTicket dog=new BuyTicket(cat);//第二個位置
dog.setName("dog");
BuyTicket bird=new BuyTicket(dog);//最後購票+
bird.setName("bird");
//有序購票,bird->dog->cat
bird.start();//儘管bird第一個開啓購票,但是也會是最後一個結束購票
dog.start();
cat.start();
/*運行結果:
* dog--正在購票--------
*bird--正在購票--------
cat--正在購票--------
cat--購票完成--------
dog--購票完成--------
bird--購票完成--------
*/
}
}
- 守護線程和資源釋放
守護線程:設置當前線程的setDaemon(true),默認創建線程是非守護線程;守護線程時,當主線程結束後,守護線程也跟着死亡。
資源釋放:1、Object的finalize()方法,可以寫資源釋放,但是不推薦,有時不會執行。比如線程直接stop()
2、推薦 try-finally 資源釋放,但是有個特列是當前線程是守護線程,主線程結束後,子線程如果不是interrupt 標誌結束,子線程死亡有時不會調用finally 代碼塊。所以推薦不管是否是守護線程,線程結束都要使用 interrupt 標誌 結束線程
代碼案例:
package other.multithread;
/**
* @ClassName DaemonTest
* @Description des
* @Author zyk
* @Date 2019/9/26
* @Version 1.0
**/
public class DaemonTest {
private static class DaemonThread extends Thread {
@Override
public void run() {
try {
while (true) {
System.out.println("-----------working...------------");
if (Thread.currentThread().isInterrupted()) {
System.out.println("============線程結束==================");
break;
}
}
} finally {
//通過finally 進行資源釋放;如果是守護線程,線程結束finally有時還是不會執行
System.out.println("=============finally-資源釋放執行=============");
}
}
@Override
protected void finalize() throws Throwable {
//Object finalize 方法,可以完成資源釋放,但是線程停止則有可能不會調到finalize()
System.out.println("=============finalize-資源釋放執行=============");
}
}
public static void main(String[] args) throws InterruptedException {
DaemonThread daemonThread = new DaemonThread();
//設置守護進程後,開啓線程的線程關閉後,守護線程也會結束;默認非守護線程
daemonThread.setDaemon(true);
daemonThread.start();
Thread.sleep(20);
//通過標誌位暫停線程,如果不interrupt(),當前線程是守護線程,主線程結束子線程也跟着結束,此時finally中 資源釋放有時就不會執行
//daemonThread.interrupt();
System.out.println("===========main線程結束=============");
}
}