您好,登錄后才能下訂單哦!
java中怎么實現多線程 ,針對這個問題,這篇文章詳細介紹了相對應的分析和解答,希望可以幫助更多想解決這個問題的小伙伴找到更簡單易行的方法。
理解多線程先要理解線程,理解線程先要理解進程。
一個正在執行的程序。
每個進程的執行都有一個執行的順序,順序是一個執行路徑,也叫一個控制單元。
進程中獨立的控制單元稱為線程。
線程控制進程的執行。
進程中只要有一個線程在執行,進程就不會結束。
一個進程中至少存在一個線程。
Java 虛擬機啟動時,會有一個 java.exe 的執行程序,也就是一個進程。
這個進程中至少存在一個線程負責 java 程序的執行,這個線程的運行代碼存在 main 方法中,這個線程稱之為主線程。
JVM 啟動時除了執行一個主線程,還會啟動負責垃圾回收機制的線程。
在一個進程中有多個線程執行的方式,稱為多線程。
多線程能讓程序產生同時運行的效果,可以提高程序執行的效率。
java.exe 進程執行主程序時,如果程序的代碼非常多,在堆內存中會產生很多對象,而對象調用完后就會變成垃圾。如果垃圾過多的話,可能會導致堆內存出現內存不足的現象,影響程序的運行。這種情況下,如果只有一個線程在運行處理的話,程序執行的效率非常低;如果有多個線程在幫助處理的話,程序執行的效率將大大的提高。
例如:垃圾回收機制的線程在幫助進行垃圾回收的話,那堆內存空間的釋放將快很多。
例如:
PC 上有很多程序“同時”進行,看起來好像是 CPU “同時”處理所有程序似的,其實在同一時刻,單核的 CPU 只能運行一個程序,看起來“同時”運行的效果,實際上只是 CPU 在多個線程之間做快速切換的動作而已。
CPU 執行哪個程序,或者說是哪個程序搶到了 CPU 的執行權,哪個程序就執行,CPU 不會只執行一個線程,執行完一個后,會執行另一個,或者說是另一個線程搶走了 CPU 的執行權。至于如何執行是由 CPU 所決定。
CPU 執行哪個程序,是毫無規律的,這是多線程的特性:隨機性。
創建線程的方式:繼承和實現。
Java 中已經提供了對線程描述的類 —— Thread。繼承 Thread 類,重寫(覆蓋)run 方法,創建線程。
步驟:
啟動線程,調用 run 方法。
注:如果對象直接調用 run 方法,那么就只有一個線程在執行,自定義的線程并沒有啟動。
相當于創建線程。
自定義代碼存儲在 run 方法中,讓線程執行。
定義類繼承 Thread;
重寫(覆蓋)Thread 中的 run 方法;
創建自定義類的實例對象;
實例對象調用線程的 start 方法。
重寫(覆蓋)run 方法的原因:Thread 類用于描述線程,類中定義了一個功能,用于存儲線程要執行的代碼,存儲這個功能的就是 run 方法。總而言之,Thread 中的 run 方法用于存儲線程要執行的代碼。
例如:
結果如下
注:線程是隨機、交替執行的,每次運行的結果都不同
使用繼承 Thread 創建線程的方式有弊端,就是如果類繼承了其它的父類,就無法使用 Thread 來創建線程,于是便有了通過實現 Runnable 接口來創建線程。實現 Runnable 接口,重寫(覆蓋)run 方法,創建線程。
步驟:
start 方法會自動調用 Runnable 子類的 run 方法。
將 Runnable 的子類對象傳遞給 Thread 的構造函數的原因:
自定義的 run 方法所屬的對象是 Runnable 的子類對象,要讓線程去指定對象的 run 方法,就必須明確 run 方法所屬的對象。
自定義代碼存儲在 run 方法中,讓線程執行。
定義類實現 Runnable;
重寫(覆蓋)Runnable 中的 run 方法;
通過 Thread 類創建線程對象。
將 Runnable 的子類對象作為實際參數傳遞給 Thread 的構造函數;
調用 Thread 中的 start 方法啟動線程。
好處:避免了單繼承的局限性。(定義線程時,建議優先使用)
例如:
結果如下:
注:線程是隨機、交替執行的,每次運行的結果都不同。
繼承:線程代碼存儲在 Thread 子類的 run 方法中。
實現:線程代碼存儲在 Runnable 子類的 run 方法中。
被創建:等待啟動,調用 start 啟動。
運行狀態:具有執行資格和執行權。
臨時狀態(阻塞):具有執行資格,但沒有執行權。
凍結狀態:遇到 sleep(time) 方法和 wait() 方法時,失去執行資格和執行權;sleep 方法的時間結束或調用 notify() 方法時,獲得執行資格,變為臨時狀態(阻塞)。
消亡狀態:調用 stop() 方法或 run 方法結束。
注:線程從創建狀態到了運行狀態后,再次調用 start() 方法時,已經沒有任何意義,Java 運行時會提示線程狀態異常。
當多條語句操作同一個線程的共享數據時,一個線程對多條語句只執行了一部分,沒執行完成時,另外的線程參與執行,會導致共享數據的錯誤異常,也就是線程的安全問題。
總而言之:
線程的隨機性。
多個線程訪問出現延遲。
注:線程的安全問題在理想狀態下,一般不容易出現,但是一旦出現線程的安全問題,將會對程序軟件造成非常大的影響。
對于線程的安全問題,在對多條操作共享數據的語句時,只讓一個線程執行完,再讓下個線程去執行,每條線程在執行的過程中,其它線程都不可以參與執行。
Java 中提供了專業的解決辦法 —— synchronized(同步)。
解決的方式:同步代碼塊和同步函數。(均是使用關鍵字 synchronized 實現)
格式:
synchronized(對象){ 需要被同步的代碼; }
同步之所以可以解決線程的安全問題,根本原因在于對象上,對象如果加了同步鎖,持有鎖的線程可以在同步中執行,沒持有鎖的線程即使獲取 CPU 的執行權,也無法進入去執行,因為沒有獲取到同步鎖。
例如:
同步代碼塊
前提:
必須有兩個或以上的線程;
必須是多個線程使用同一個鎖。
利與弊:
利:解決了多線程的安全問題。
弊:多個線程均需要判斷鎖,消耗資源,影響效率。
如何尋找多線程的安全問題?
明確共享數據;
明確哪些代碼是多線程運行的代碼;
明確多線程運行的代碼中哪些語句是操作共享數據的。
同步函數被靜態所修飾后,使用的同步鎖不再是 this,因為靜態函數中不可以定義 this,靜態進入內存時,內存中沒有本類對象,但一定存在類所對應的字節碼文件對象。
例如:類名.class(對象的類型是 Class)
靜態函數所使用的同步鎖就是所在類的字節碼文件對象。
類名.class
例如:
同步中嵌套同步時,有可能出現死鎖現象。
例如:
說明:程序卡死,無法繼續執行。
多個線程操作同一個資源,但是操作的動作不相同,就是線程間通信。
同步操作同一個資源
例如:
問題點:
需要喚醒對方線程時,如果只用 notify(),容易出現只喚醒本方線程的情況,會導致程序中所有線程都處于等待狀態。
wait():釋放 CPU 的執行權,釋放同步鎖。
sleep():釋放 CPU 的執行權,不釋放同步鎖。
例如:同一個鎖上 wait 的線程,只能被同一個鎖上的 notify 喚醒。
這些方法存在于同步中;
使用這些方法時必須要有標識所屬的同步鎖;
鎖可以是任意的對象,任意對象調用的方法一定要定義在 Object 類中。
wait()、notify()、notifyAll() 用來操作線程的,為什么是定義在 Object 類中呢?
wait() 和 sleep() 有什么區別呢?
為什么要定義 notifyAll()?
JDK 5 及以上版本中提供了多線程同步鎖的升級解決方案
將 synchronized(同步)替換成 Lock,將 Object 類中的 wait()、notify()、notifyAll() 替換成 Condition 對象。
Condition 對象可通過 Lock(鎖)進行獲取,并且支持多個相關的 Condition 對象。
例如:
說明:只要在主函數或者其它線程中,對標記 flag 賦值 false,就可以讓 run() 方法結束,線程停止。
特殊情況:當線程處于凍結狀態時,無法讀取到 run() 方法中的代碼,線程就無法停止。
需要對線程的凍結狀態進行清除,強制讓線程恢復運行,Thread 類中提供了 interrupt();
例如:
某些代碼需要同時執行時,可用單獨的線程封裝,多線程運行執行。
例如
join();
當 A 線程執行到了 B 線程的 join(); 方法時,A 線程等待,B 線程執行完后,A 線程才繼續執行(此時的 B 線程與其它線程交替執行)。
臨時加入線程去執行:
setPriority();
MIN_PRIORITY:最低優先級 1
MAX_PRIORITY:最高優先級 10
NORM_PRIORITY:默認優先級
設置優先級:
yield();
可以暫停當前線程,讓其它線程執行。
關于java中怎么實現多線程 問題的解答就分享到這里了,希望以上內容可以對大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關注億速云行業資訊頻道了解更多相關知識。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。