Java | 線程和進程,創建線程

一、線程與進程

線程定義

進程中執行的一個代碼段,來完成不同的任務

組成:線程ID,當前指令指針(PC),寄存器集合(存儲一部分正在執行線程的處理器狀態的值)和堆棧

進程定義

執行的一段程序,一旦程序被載入到內存中準備執行就是一個進程

組成:文本區域(存儲處理器執行的代碼)、數據區域(存儲變量和進程執行期間的動態分配的內存)和堆棧(存儲著活動過程調用的指令和本地變量)

例如Windows系統中運行的一個exe就是一個進程

進入任務管理器,可以查看系統運行的進程,以及每個進程中的線程數

線程與進程的關系

1.? 進程是系統內存分配的最小單位,線程是系統調度的最小單位

進程擁有自己的內存空間,因為線程是屬于進程的,多線程直接共享該進程中內存,提高了程序的運行效率

2.? 一個程序至少有一個進程,一個進程中包括一條 or 多條線程,線程不能獨立于進程

在Java中,每次程序運行至少啟動2個線程:一個是main線程,一個是垃圾收集線程。因為每當使用 Java 命令執行一個類時,都會啟動一個JVM,每一個 JVM 實際上就是在操作系統中啟動了一個進程

3.? 進程與線程都可以并發執行

問題:如何了解 “并發” 執行 ,它與 “并行”執行一樣嗎?

并行執行:從宏觀和微觀的角度,都是同時執行的

并發執行:從宏觀角度,似乎是同時執行;但從微觀角度,不是同時執行

操作系統采取時間片的機制,使多個進程(線程)快速切換執行,在宏觀上就有并行執行的錯覺

在單核情況下,不存在并行執行;但在多核情況下,進程(線程)分布在不同的CPU中,可以并行執行程序

二、線程的生命周期

線程是一個動態執行的過程

1.? 新建狀態 New

創建線程對象,進入新建狀態,此時線程屬于 not? alive,直到執行 start()

創建線程: 使用 new 關鍵字和 Thread 類或其子類, 例如:Thread?? t? = new? MyThread();

2.? 就緒狀態 Runnable

調用線程對象的 start() 方法,進入就緒狀態,此時線程屬于 alive ,但還未進入執行,只是做好了被 CPU 調度的準備

3.? 運行狀態 Running

當線程獲取到CPU,進入運行狀態,線程的 run() 方法才開始被執行,此時線程屬于 alive

只有當線程處于就緒狀態,才能被CPU調度,所以就緒狀態是運行狀態的唯一入口

4.? 阻塞狀態 Blocked

處于運行狀態的線程,由于某種原因,放棄使用CPU,停止運行,進入阻塞狀態,此時線程屬于 alive

同步阻塞

同步鎖 synchronized,當某線程占有了該同步鎖, 則其他線程就不能進入到同步鎖中,則這些線程就會進入阻塞狀態

當在阻塞隊列的線程獲取到同步鎖時,才能進入到就緒狀態等待被調度

等待阻塞:(理解得有點繞)

調用線程的 wait() ,線程進入等待狀態,此時會釋放占用的 CPU 資源和鎖(wait()方法需要在鎖中使用)

當被其他線程調用 notify() 喚醒之后,需要重新獲取對象的鎖,所以會先進入Blocked狀態,才會進入就緒狀態

其他阻塞

調用線程的 sleep() 或 join() 或 發出了I/O請求,線程會進入到阻塞狀態

當 sleep() 狀態超時、join() 等待線程終止或者超時、或者 I/O 處理完畢時,線程重新進入就緒狀態

5.? 死亡狀態 Dead

當一個線程的 run() 方法運行完畢 or 被中斷 or 被異常退出,該線程進入死亡狀態

三、線程的創建

-?? 實現 Runnable 接口,實例化 Thread 類(線程無返回值)

- ? 繼承 Thread 類,重寫 Thread 的 run() 方法(線程無返回值)

-?? 實現 Callable 接口,通過 FutureTask 包裝器創建線程(線程有返回值)

1.? 實現 Runnable 接口,實例化 Thread 類(線程無返回值)

step1:? 創建一個類,例如 RunnableThread,實現 Runnable 接口

step2:? 實例化 RunnableThread 對象, 創建 Thread 對象,將 RunnableThread 作為參數傳給 Thread 類的構造函數,然后通過 Thread.start() 方法啟動線程

運行結果

問題: 為什么創建 RunnableThread 對象后,需要將它和 Thread 對象進行關聯?

查看 Runnable 接口的源代碼,可以看到 Runnable 接口只有一個 run() 方法,所以需要通過 Thread 類的 start() 方法來啟動線程

啟動線程后,Thread 類中的 run() 方法會先判斷傳入的 target Runnable 對象的 run() 方法是否為空,若不為空,則調用 target Runnable 對象的 run() 方法

而且,RunnableThread 類實現 Runnbale 接口中不能直接使用 Thread 類中的方法,需要先獲取到Thread 對象后,才能調用 Thread 方法

2.? 繼承 Thread 類,重寫 Thread 的 run() 方法(線程無返回值)

step1:? 創建一個類,例如 MyThread,繼承 Thread 類,重寫 Thread 的 run() 方法

step2:? 實例化 MyThread 對象,直接調用 start() 方法啟動線程

運行結果

問題:實現 Runnable 接口 和 繼承 Thread 類,運行結果不一樣,這是為什么?

繼承 Thread 類和實現 Runnable 接口實現多線程,會發現這是兩個不同的實現多線程

繼承 Thread 類是多個線程分別完成自己的任務

實現 Runnable 接口是多個線程共同完成一個任務,其實用繼承Thread類也可以實現,只是比較麻煩

這樣的話,實現 Runnable 接口比繼承 Thread 類具有一定的優勢

1)適合多個相同的程序代碼的線程去處理同一個資源

2)可以避免 Java 中的單繼承的限制

當一個類繼承 Thread 類后,則不能在繼承別的類,而接口比較靈活,可以實現多個接口,而且實現接口了還可繼續繼承一個類

3)增加程序的健壯性,代碼可以被多個線程共享,代碼和數據獨立

參考鏈接:https://www.cnblogs.com/CryOnMyShoulder/p/8028122.html

?????????????????https://www.cnblogs.com/xubiao/p/5418141.html

3.? 實現 Callable 接口,通過 FutureTask 包裝器創建線程(線程有返回值)

step1:? 創建一個類,例如 CallableThread,實現 Callable 接口,重寫 Callable 接口的 call() 方法

step2:? 實例化 CallableThread 對象,使用 FutureTask 類來包裝 CallableThread 對象

然后 FutureTask 對象作為參數傳給 Thread 類的構造函數,通過 Thread.start() 方法啟動線程

使用 FutureTask.get() 得到 Callable 接口的 call() 方法的返回值

返回結果

Callable 和 Runnable 相似,類實例都需要被 Thread 執行,但 Callable 接口能返回一個值或者拋出一個異常,Runnable 不能

實現 Callable 接口需要重寫其唯一的 call() 方法

FutureTask 實現了 Runable 接口 和 Future 接口,所以如果想 Callable 實例作為 Thread 的執行體就必須通過 FutureTask 來作為橋梁


參考鏈接:https://www.cnblogs.com/ganchuanpu/p/7704468.html

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 進程和線程 進程 所有運行中的任務通常對應一個進程,當一個程序進入內存運行時,即變成一個進程.進程是處于運行過程中...
    勝浩_ae28閱讀 5,137評論 0 23
  • 進程和線程 進程 所有運行中的任務通常對應一個進程,當一個程序進入內存運行時,即變成一個進程.進程是處于運行過程中...
    小徐andorid閱讀 2,830評論 3 53
  • 單任務 單任務的特點是排隊執行,也就是同步,就像再cmd輸入一條命令后,必須等待這條命令執行完才可以執行下一條命令...
    Steven1997閱讀 1,201評論 0 6
  • 線程概述 線程與進程 進程 ?每個運行中的任務(通常是程序)就是一個進程。當一個程序進入內存運行時,即變成了一個進...
    閩越布衣閱讀 1,019評論 1 7
  • 上回講到,用戶端如何派單,今天要講的,是派單完成后,生成訂單的界面,及支付輸入密碼的頁面設計。 我的訂單可...
    一盒甜豆奶_閱讀 266評論 0 3