哈哈,上邊扯了好大一下子犢子才繞到java語法這😅。沒辦法呀,你不瞭解故事背景直接看劇情老是會有些懵逼的~java語言中的線程是對操做系統線程的一種抽象,有些地方可能不太一致,遇到了再和你們說哈~java
咱們以前說過,main
方法是程序入口,咱們對已經編譯好的class文件
調用java
命令時就能夠運行一個java程序。這個過程當中,其實系統自動爲咱們建立了一個進程和一個線程,並且這個線程的名字就叫作main
。main線程
是用來執行咱們的程序的,不過系統還會爲咱們建立一些輔助線程
來幫助main線程
的執行,如今就先不說它們是啥了,等遇到了再說哈~編程
除了系統本身建立的這個main線程
之外,咱們還能夠本身在程序裏建立一些線程。不過咱們前邊說過,線程實際上是去執行任務
的,因此咱們先看怎麼定義任務
。ide
java中的任務被抽象成了一個Runnable接口
:字體
public interface Runnable { public void run(); }
咱們的自定義任務
須要去實現這個接口,並把任務
的詳細內容寫在覆蓋的run
方法裏,好比咱們定義一個輸出一個字符串的任務:操作系統
public class PrintTask implements Runnable { @Override public void run() { System.out.println("輸出一行字"); } }
看到了吧,定義一個任務
就是這麼簡單哈~不過光有任務沒啥卵用,須要建立一個線程去運行這個任務
線程
java中的Thread
類來表明一個線程,咱們須要關注它的這幾種構造方法:設計
Thread(Runnable target, String name)
在建立線程對象的時候傳入須要執行的任務
以及這個線程的名稱。code
Thread(Runnable target)
Thread(String name)
Thread()
執行任務對象
Thread
類的start()
方法負責開始執行一個線程
,讓一個線程運行起來有這麼兩種方法:繼承
任務
:public class Test { public static void main(String[] args) { new Thread(new PrintTask()).start(); } }
執行結果是:
輸出一行字
Thread
類並覆蓋run
方法:Thread
類自己就表明了一個Runnable
任務,咱們看Thread類的定義:
public class Thread implements Runnable { private Runnable target; @Override public void run() { if (target != null) { target.run(); } } // ... 爲省略篇幅,省略其餘方法和字段 }
其中的target
就是在構造方法裏傳入的,若是構造方法不傳這個字段的話,很顯然run
方法就是一個空實現,因此若是咱們想運行這個線程,就繼承它而且覆蓋一下run
方法吧:
public class PrintThread extends Thread { @Override public void run() { System.out.println("輸出一行字"); } }
由於PrintThread中已經有一個任務了,因此直接調用start方法運行它就好:
public class Test { public static void main(String[] args) { new PrintThread().start(); } }
執行結果是:
輸出一行字
這兩種執行任務的方法說不上誰好誰壞,可是使用繼承Thread
類而且覆蓋run
方法的方式把線程和任務給弄到了一起了,不可分割了,也就是所謂的耦合了,因此咱們平時更傾向於使用任務和線程分開處理的第1種執行任務的方式。固然,有時候爲了演示的方便,也是會使用繼承Thread
類而且覆蓋run
方法的方式~
Thread類提供了許多方法來方便咱們獲取線程的信息或者控制線程,下邊來一下都有哪些重要的方法吧:
獲取線程ID
System.out.println(new Thread().getId()); System.out.println(new Thread().getId()); System.out.println(new Thread().getId());
執行結果:
10 11 12
獲取和設置線程名稱
固然,咱們也能夠經過構造方法去設置Thread的名稱:
Thread t1 = new Thread("t1"); Thread t2 = new Thread(); t2.setName("t2"); System.out.println("t1線程的名稱是:" + t1.getName()); System.out.println("t2線程的名稱是:" + t2.getName());
執行結果是:
t1線程的名稱是:t1 t2線程的名稱是:t2
設置線程的優先級
咱們知道處理器會從就緒的隊列裏挑一個已經就緒的線程去執行,每一個線程均可以有不一樣的優先級,優先級越高,越容易被處理器選中執行。
java中的優先級是用一個正數來表示,共有1~10個等級,其中,設計java的大叔們用了是三個靜態變量表示咱們經常使用的:
通常狀況下,咱們就用這三個變量去表示優先級就夠用了。
下面看個例子:
Thread t1 = new Thread("t1"); System.out.Println("t1線程的優先級是:" + t1.getpriority()); t1.setPriority(Thread.MAX_PRIORITY); System.out.Println("t1線程的優先級是:" + t1.getpriority());
注意:線程優先級並不意味着得不處處理器執行,而只是執行的頻次低一點而已。並且線程的優先級通常不用咱們主動去設置,因此這兩個方法對咱們來講基本沒啥用~
休眠
若是想在線程執行過程當中讓程序停一段時間以後再執行,這個中止一段時間也叫作休眠,就好像睡一段時間而後醒來。能夠經過sleep()方法來實現休眠:
程序在指定的毫秒數加納秒數內讓當前正在執行的線程休眠,也就是暫停執行。
程序在指定的毫秒數加納秒數內讓當前正在執行的線程休眠,也就是暫停執行。
你們注意到這個sleep方法是一個靜態方法,它會讓當前線程暫停指定的時間。這個所謂的暫停,或者說休眠其實只是把正在運行的線程阻塞掉,放到阻塞隊列裏,等指定的時間一到,再從阻塞的隊列裏出來而已。另外,這個方法有InterruptedException的異常,說明咱們在調用的時候須要catch一下:
public static void main(String[] args) { System.out.println(1); try { Thread.sleep(1000L); } catch (InterruptedException e) { throw new RuntimeException(e); } System.out.println(2); try { Thread.sleep(1000L); } catch (InterruptedException e) { throw new RuntimeException(e); } System.out.print(3); }
你們在執行的過程當中會發現,每隔一秒會輸出一個數字。另外,因爲是在main方法中調用的sleep方法,因此其實休眠的是main線程,你們能夠試試在本身的線程裏休眠哈~
讓出本次處理器的時間片
咱們知道線程是處理器時間片的分配單位。不一樣的線程排着對等着處理器賞賜給一個時間片用來執行一下子代碼。若是一個線程已經得到了一個時間片正在執行,它忽然不想執行了,能夠放棄這次的時間片時間,先讓處理器給別的線程分配一個時間片,而他參與下一輪的時間分配。
舉個例子:好比說咱們把處理器比做皇帝,把各個線程比做妃子,各個妃子爭相讓皇帝寵幸,爲了公平,皇帝只能一次寵幸一個妃子五分鐘。皇帝怎麼挑選妃子有他本身的小算盤,可是,每一個五分鐘都會選一個去陪她。此時有一個妃子被皇帝挑選中了,但是她配了皇帝五分鐘有些尿急,因此她主動說先放棄此次寵幸,先出去放放水,而後再排隊被挑選吧。因此只陪了皇帝兩分鐘的她便退出了這次寵幸,放水回來後再加入到覅誒這大軍中等待皇帝挑選。
這個yield方法只是建議處理器不要在這次時間片時間內繼續執行本線程,最後實際怎麼着還不必定呢,另外,yield是一個靜態方法,表示讓出當前線程本次時間片的時間。
也就是說你想放棄就放棄是不可能的,這還得看皇帝的心情~