线程最最基础的知识
什么是線程
試想一下沒有線程的程序是怎么樣的?百度網盤在上傳文件時就無法下載文件了,得等文件上傳完成后才能下載文件。這個我們現在看起來很反人性,因為我們習慣了一個程序同時可以進行運行多個功能,而這些都是線程的功勞。
之前的文章?進程知多少?中講到,為了實現多個程序并行執行,引入了進程概念。現在引入線程是為了讓一個程序能夠并發執行。
線程的組成
線程ID:線程標識符。
當前指令指針(PC):指向要執行的指令。
寄存器集合:存儲單元寄存器的集合。
堆棧:暫時存放數據和地址,一般用來保護斷點和現場。
線程與進程區別
線程和進程之間的區別,我覺得可以用這個例子來看出兩者的不同,進程就是一棟房子,房子住著 3 個人,線程就是住在房子里的人。進程是一個獨立的個體,有自己的資源,線程是在進程里的,多個線程共享著進程的資源。
線程狀態
我們看到 Java 源代碼里面,線程狀態的枚舉有如下 6 個。
public enum State {//新建狀態 NEW,//運行狀態 RUNNABLE,//阻塞狀態 BLOCKED,//等待狀態 WAITING,//等待狀態(區別在于這個有等待的時間) TIMED_WAITING,//終止狀態 TERMINATED;}下面給這 6 個狀態一一做下解釋。
NEW:新建狀態。在創建完 Thread ,還沒執行 start() 之前,線程的狀態一直是 NEW。可以說這個時候還沒有真正的一個線程映射著,只是一個對象。
RUNNABLE:運行狀態。線程對象調用 start() 之后,就進入 RUNNABLE 狀態,該狀態說明在 JVM 中有一個真實的線程存在。
BLOCKED:阻塞狀態。線程在等待鎖的釋放,也就是等待獲取 monitor 鎖。
WAITING:等待狀態。線程在這個狀態的時候,不會被分配 CPU,而且需要被顯示地喚醒,否則會一直等待下去。
TIMED_WAITING:超時等待狀態。這個狀態的線程也一樣不會被分配 CPU,但是它不會無限等待下去,有時間限制,時間一到就停止等待。
TERMINATED:終止狀態。線程執行完成結束,但不代表這個對象已經沒有了,對象可能還是存在的,只是線程不存在了。
線程既然有這么多個狀態,那肯定就有狀態機,也就是在什么情況下 A 狀態會變成 B 狀態。下面就來簡單描述一下。
結合下圖,我們 new 出線程類的時候,就是 NEW 狀態,調用 start()?方法,就進入了 RUNNABLE 狀態,這時如果觸發等待,則進入了 WAITING 狀態,如果觸發超時等待,則進入 TIMED_WAITING 狀態,當訪問需要同步的資源時,則只有一個線程能訪問,其他線程就進入 BLOCKED 狀態,當線程執行完后,進入 TERMINATED 狀態。
圖片來源于網路,侵刪
其實在 JVM 中,線程是有 9 個狀態,如下所示,有興趣的同學可以深入了解一下。
javaClasses.hppenum ThreadStatus { NEW = 0, RUNNABLE = JVMTI_THREAD_STATE_ALIVE + // runnable / running JVMTI_THREAD_STATE_RUNNABLE, SLEEPING = JVMTI_THREAD_STATE_ALIVE + // Thread.sleep() JVMTI_THREAD_STATE_WAITING + JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT + JVMTI_THREAD_STATE_SLEEPING, IN_OBJECT_WAIT = JVMTI_THREAD_STATE_ALIVE + // Object.wait() JVMTI_THREAD_STATE_WAITING + JVMTI_THREAD_STATE_WAITING_INDEFINITELY + JVMTI_THREAD_STATE_IN_OBJECT_WAIT, IN_OBJECT_WAIT_TIMED = JVMTI_THREAD_STATE_ALIVE + // Object.wait(long) JVMTI_THREAD_STATE_WAITING + JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT + JVMTI_THREAD_STATE_IN_OBJECT_WAIT, PARKED = JVMTI_THREAD_STATE_ALIVE + // LockSupport.park() JVMTI_THREAD_STATE_WAITING + JVMTI_THREAD_STATE_WAITING_INDEFINITELY + JVMTI_THREAD_STATE_PARKED, PARKED_TIMED = JVMTI_THREAD_STATE_ALIVE + // LockSupport.park(long) JVMTI_THREAD_STATE_WAITING + JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT + JVMTI_THREAD_STATE_PARKED, BLOCKED_ON_MONITOR_ENTER = JVMTI_THREAD_STATE_ALIVE + // (re-)entering a synchronization block JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER, TERMINATED = JVMTI_THREAD_STATE_TERMINATED};Java 線程實現
下面講一講在 Java 中如何創建一個線程。眾所周知,實現 Java 線程有 2 種方式:繼承 Thread 類和實現 Runnable 接口。
繼承 Thread 類
繼承 Thread 類,重寫 run()?方法。
class MyThread extends Thread {@Override public void run() { System.out.println("MyThread"); } }實現 Runnable 接口
實現 Runnable 接口,實現 run()?方法。
class MyRunnable implements Runnable {public void run() { System.out.println("MyRunnable"); } }這 2 種線程的啟動方式也不一樣。MyThread 是一個線程類,所以可以直接 new 出一個對象出來,接著調用 start()?方法來啟動線程;而 MyRunnable 只是一個普通的類,需要 new 出線程基類 Thread 對象,將 MyRunnable 對象傳進去。
下面是啟動線程的方式:
public class ThreadImpl {public static void main(String[] args) { MyThread myThread = new MyThread(); Thread myRunnable = new Thread(new MyRunnable()); System.out.println("main Thread begin"); myThread.start(); myRunnable.start(); System.out.println("main Thread end"); } }打印結果如下:
main Thread beginmain Thread endMyThreadMyRunnable看這結果,不像咱們之前的串行執行依次打印,主線程不會等待子線程執行完。
敲重點:不能直接調用 run(),直接調用 run()?不會創建線程,而是主線程直接執行 run()?的內容,相當于執行普通函數。這時就是串行執行的。看下面代碼:
public class ThreadImpl {public static void main(String[] args) { MyThread myThread = new MyThread(); Thread myRunnable = new Thread(new MyRunnable()); System.out.println("main Thread begin"); myThread.run(); myRunnable.run(); System.out.println("main Thread end"); } }打印結果:
main Thread beginMyThreadMyRunnablemain?Thread?end 從結果看出只是串行的,但看不出沒有線程,我們看下面例子來驗證直接調用 run() 方法沒有創建新的線程,使用 VisualVM 工具來觀察線程情況。我們對代碼做一下 修改,加上 Thread.sleep(1000000) 讓它睡眠一段時間,這樣方便用工具查看線程情況。調用 run()?的代碼:
public class ThreadImpl {public static void main(String[] args) { MyThread myThread = new MyThread(); myThread.setName("MyThread"); Thread myRunnable = new Thread(new MyRunnable()); myRunnable.setName("MyRunnable"); System.out.println("main Thread begin"); myThread.run(); myRunnable.run(); System.out.println("main Thread end"); try { Thread.sleep(1000000); } catch (InterruptedException e) { e.printStackTrace(); } } }class MyThread extends Thread {@Override public void run() { System.out.println("MyThread"); try { Thread.sleep(1000000); } catch (InterruptedException e) { e.printStackTrace(); } } }class MyRunnable implements Runnable {public void run() { System.out.println("MyRunnable"); try { Thread.sleep(1000000); } catch (InterruptedException e) { e.printStackTrace(); } } }運行結果:
main Thread beginMyThread只打印出 2 句日志,觀察線程時也只看到 main 線程,沒有看到 MyThread 和 MyRunnable 線程,印證了上面咱們說的:直接調用 run()?方法,沒有創建線程。
下面我們來看看有調用 start()?的代碼:
public class ThreadImpl {public static void main(String[] args) { MyThread myThread = new MyThread(); myThread.setName("MyThread"); Thread myRunnable = new Thread(new MyRunnable()); myRunnable.setName("MyRunnable"); System.out.println("main Thread begin"); myThread.start(); myRunnable.start(); System.out.println("main Thread end"); try { Thread.sleep(1000000); } catch (InterruptedException e) { e.printStackTrace(); } } }運行結果:
main Thread beginmain Thread endMyThreadMyRunnable所有日志都打印出來了,并且通過 VisualVM 工具可以看到 MyThread 和 MyRunnable 線程。看到了這個結果,切記創建線程要調用 start()?方法。
————
編輯?∑Gemini
?來源:CSDN云計算
?92年的小哥,985的特聘教授:3年博士期間發表40篇SCI
?數學家家譜之可視化
?曾撿破爛為生,窮到沒飯吃,現在身家395億,給員工發3億年終獎!
?“四院院士”新任北大理學部主任,這位頂尖科學家有多牛?
?假如有人把支付寶存儲服務器炸了(物理炸),大眾在支付寶里的錢是不是就都沒有了呢?
?又一位!發40篇SCI,90后博士受聘985教授
算法數學之美微信公眾號歡迎賜稿
稿件涉及數學、物理、算法、計算機、編程等相關領域,經采用我們將奉上稿酬。
投稿郵箱:math_alg@163.com
總結
- 上一篇: “骗子”成民企院士第一人:把认真当信仰,
- 下一篇: 2名数学家或发现史上最快超大乘法运算法,