這篇文章主要是對線程有基本瞭解的夥伴當作筆記來瞅一眼。關於什麼是線程等問題不會進行描述。
一、創建線程
創建線程的方式主要有倆種,說是倆種,我們點進源碼去看一下,其實就是一種方式,因爲Thread
類就是實現
了Runnable
接口。換湯不換藥。
第一種:通過繼承Thread類重寫run方法:
public class CreateThread {
public static void main(String[] args) {
Thread t1 = new MyThread();
//啓動線程
t1.start();
}
}
class MyThread extends Thread{
@Override
public void run() {
System.out.println("創建線程!");
}
}
簡寫方式:
//匿名內部類
new Thread(){
@Override
public void run() {
System.out.println("創建線程!");
}
}.start();
第二種:通過實現Runnable接口重寫run方法:
public class createThread {
public static void main(String[] args) {
//實例化任務
Runnable r1 = new MyRunnable();
//創建線程同時指定任務
Thread t1 = new Thread(r1);
//等於Thread t1 = new Thread(new MyRunnable)
t1.start();
}
}
class MyRunnable implements Runnable{
@Override
public void run() {
System.out.println("創建線程!");
}
}
簡寫方式:
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("創建線程!");
}
}).start();
最簡方式 Lambda表達式:
new Thread(()-> System.out.println("創建線程!")).start();
二、阻塞線程
Thread類提供靜態方法sleep(),讓運行這個方法的線程阻塞指定毫秒。
注:這裏需要捕捉阻塞中斷異常!怎麼理解呢,就是在睡覺時醒來不是自然醒,被外力弄醒。
new Thread(()-> {
try {
//線程阻塞2秒
Thread.sleep(2000);
//2秒之後執行打印
System.out.println("我自由了!");
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
有睡眠方法,肯定要有叫醒方法呀,也就是中斷線程阻塞。通過線程實例調用interrupt
方法(會觸發異常),我這裏圖方便寫的匿名類,無法調用就不調用了。算了,貼一下代碼瞅一眼吧。
Thread t1 = new Thread(()->{
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
t1.start();
new Thread(()->{
System.out.println("我要打斷t1線程睡眠!");
t1.interrupt();
}).start();
運行結果:
我要打斷t1線程睡眠!
java.lang.InterruptedException: sleep interrupted
三、守護線程
何爲守護線程?
守護線程又稱爲後臺線程,在默認情況下我們創建的線程都爲普通線程。在線程啓動之前
調用setDaemon(true)
方法纔會將普通線程變爲守護線程。
那麼守護線程與普通線程的區別是?
用我們的兵哥哥舉個例:我們這些普通的人民就是屬於普通線程,兵哥哥就是我們的守護線程。不管發生什麼危險與事故,永遠都是兵哥哥在我們的背後守護我們離開,待我們所有普通人民離開後,兵哥哥們纔會有序撤離。這樣應該就理解了:在普通線程全部結束後,所有正在運行的守護線程會被強制停止。進程就會結束。
Thread t1 = new Thread(()->{
while(true){
System.out.println("我是守護線程!");
}
});
//在線程調用之前設置爲守護線程
t1.setDaemon(true);
t1.start();
new Thread(()->{
for (int i = 0; i < 100; i++) {
System.out.println("我是普通線程!");
}
}).start();
運行結果:
我們在t1線程中寫的是一個死循環,運行之後會發現普通線程結束後
我們的t1死循環線程也結束了!
四、同步線程
我們應該知道,線程屬於併發編程,每一個線程都是在埋頭各幹各的任務。如果我們有一個需求需要線程同步應該如何做?Thread類中提供join()
方法(需要捕捉中斷異常)來協調多個線程同步。點開該方法看一下源碼註釋:Waits for this thread to die.
意思很明顯了—>等待該線程死亡。
當然Thread類也重載了該join方法,可自行點開源碼查看。如 join(long millis)等待調用join方法的線程多少毫秒。
就舉一個我在班級中學過的一個例子:
我們打開一個網頁需要同時加載文字與圖片,在文字加載結束後,我們需要等待圖片加載完成才能將文字與圖片顯示出來。
Thread t1 = new Thread(()->{
System.out.println("開始加載圖片!");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("圖片加載完成!");
});
t1.start();
new Thread(()->{
System.out.println("開始加載文字!");
System.out.println("文字加載完成!");
try {
t1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("全部加載成功,開始顯示網頁!");
}).start();
運行結果:
注
:在使用線程的時候一定需要注意線程安全的問題!