您好,登錄后才能下訂單哦!
這篇文章主要講解了“多線程編程的三種實現方式是什么”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“多線程編程的三種實現方式是什么”吧!
進程: 是程序的一次動態執行過程,它經歷了從代碼加載、執行到執行完畢的一個完整過程,這個過程就是進程產生、發展到最終消亡的過程;
多進程: 操作系統能同時運行多個進程(程序),由于CPU
具備分時機制,在每個進程都能循環獲得自己的CPU時間片;由于CPU執行的速度非常快,使得所有的程序好像是在同時運行一樣。
進程是資源調度的基本單位,運行一個可執行程序會創建一個或多個線程,進程就是運行起來的可執行程序;
線程是程序執行的基本單位,是輕量級的進程,每個進程中都有唯一的主線程,且只能有一個,主線程和進程是相互依存的關系,主線程結束,進程也會結束。
具體實例(word):
每次啟動Word對于操作系統而言就相當于啟動了一個系統的進程,而在這個進程之上又有許多其他程序在運行(拼寫檢查等),那么對于這些程序就是一個個多線程。如果Word關閉了,則這些拼寫檢查的線程也肯定會消失,但是如果拼寫檢查的線程消失了,并不一定會讓Word的進程消失;
多插一句:如果打開兩個word文檔,則表示當前操作系統創建了兩個進程。
實現多線程需要一個線程的主體類,這個類可以繼承Thread
、實現Runnable
以及Callable
接口完成定義;
繼承結構如下:
public class Thread extends Object implements Runnable
實現接口Runnable
,所以必須實現接口中的抽象方法:
Modifier and Type | Method | Description |
---|---|---|
void | run() | 當一個實現接口Runnable的對象被用來創建線程時,啟動線程會導致對象的run方法在單獨執行的線程中被調用。 |
void | start() | 使線程開始執行;Java虛擬機調用這個線程的run方法。 |
當產生多個對象時,這些對象就會并發的執行run()方法中的代碼;
雖然多線程的執行方法都在run()方法中定義,但是在實際進行多線程啟動時并不能直接調用此方法,由于多線程時需要并發執行的,所以需要通過操作系統的資源調度才能執行,這樣多線程的啟動就必須利用Thread類中的start()方法完成,調用此方法會間接的調用run()方法。
實例:
package Java從入門到項目實戰.多線程編程.Java多線程實現; class MyThread extends Thread{ //單繼承 private String title; public MyThread(String title){ this.title = title; } //覆寫線程的run方法 @Override public void run() { for (int i = 0 ; i < 10; i++){ System.out.println(this.title+"運行,i =" +i); } } } public class Main{ public static void main(String[] args){ new MyThread("線程A").start(); //實例化線程對象并啟動 new MyThread("線程B").start(); new MyThread("線程C").start(); //對照 /*沒有開啟多線程*/ new MyThread("線程A").run(); new MyThread("線程B").run(); new MyThread("線程C").run(); } }
由效果圖可以看出,三個線程在交替執行:
假如面試題:
為什么線程啟動的時候必須調用start()方法而不是直接調用run()方法?
在本程序中,程序調用了Thread類繼承而來的start()方法后,實際上他執行的還是覆寫后的run()方法,那為什么不直接調用run()?
簡單的說下:是因為多線程需要調用操作系統的資源,在start()下有一個關鍵的部分start0()方法,并且在start0()方法上使用了navite關鍵字定義;
public synchronized void start() { if (threadStatus != 0) throw new IllegalThreadStateException(); group.add(this); boolean started = false; try { start0(); started = true; } finally { try { if (!started) { group.threadStartFailed(this); } } catch (Throwable ignore) { } } } private native void start0(); //navite
什么是navite?
navite是指:Java本機接口(Java Native Interface)簡稱:JNI;使用Java調用本機操作系統的函數功能完成一些特殊操作;
在Java中將start0()方法體交給JVM進行實現,所以這樣就會出現在windows或者在Linux中實現的start0()的是不同,不關系過程,只關心結果(是否調用了本機的操作系統的函數);
start0()作用:交由JVM進行匹配不同的操作系統,實現start0()方法體,功能:實現本機函數的調用;
具體百度、Google吧。
出現的原因:為了解決Thread實現多線程出現的單繼承問題;并且增加了函數式接口;
Modifier and Type | Method | Description |
---|---|---|
void | run() | 當一個實現接口Runnable的對象被用來創建線程時,啟動線程會導致對象的run方法在單獨執行的線程中被調用。 |
實現代碼:
class MyThread implements Runnable{ private String title; public MyThread(String title){ this.title = title; } @Override public void run() { //線程方法覆寫 for (int i = 0; i< 10;i++){ System.out.println(this.title+"運行,i"+i); } } }
啟動方式一:
Thread threadA = new Thread(new MyThread("線程A")); Thread threadB = new Thread(new MyThread("線程B")); Thread threadC = new Thread(new MyThread("線程C")); Thread threadD = new Thread(new MyThread("線程D")); Thread threadE = new Thread(new MyThread("線程E")); threadA.start(); threadB.start(); threadC.start(); threadD.start(); threadE.start();
啟動方式二:
//通過Lambal表達式定義線程主體 for(int x = 0; x < 3;x++){ String title = "線程對象-"+x; //實際上Thread傳入的類型是Runnable new Thread(()->{ //Lambda實現線程主體 for(int y = 0; y < 20; y++){ System.out.println(title+"運行,y"+y); } }).start(); }
Thread與Runnable的聯系:
繼承結構:
public class Thread extends Object implements Runnable
在之前繼承Thread類的時候實際上覆寫的還是Runnable接口的run()方法。
實現并發訪問資源:
package Java從入門到項目實戰.多線程編程.Java多線程實現; class MyThreadConcurrent implements Runnable { private int ticket = 5; @Override public void run() { for (int i = 0; i < 100; i++) { //同步操作--》從5-1票數 /*synchronized(this){ if(this.ticket > 0){ System.out.println("賣票,ticket = "+this.ticket--); } }*/ //票數亂數 if(this.ticket > 0){ System.out.println("賣票,ticket = "+this.ticket--); } } } } public class 并發資源訪問 { public static void main(String[] args) { MyThreadConcurrent thread = new MyThreadConcurrent(); new Thread(thread).start(); //第一個線程 new Thread(thread).start(); //第二個線程 new Thread(thread).start(); //第三個線程 } }
總結一句話:Thread有單繼承的局限性以及在有些情況下結構的不合理性;所以后面多線程的實現使用的都是Runnable接口。
為什么要使用Callable接口來實現多線程?
因為使用Callable接口實現彌補了Runnable實現多線程沒有返回值的問題。
繼承結構如下:
@FunctionalInterface public interface Callable<V>{ public V call() throws Exception{ } }
定義的時候可以設置一個泛型,此泛型的類型就是call()方法的返回的數據類型,好處:可以避免向下轉型的安全隱患。
線程類主體完成后,需要啟動多線程的話還是需要通過Thread類實現的,又因為我們的Callable接口與Thread沒有聯系,所以我們需要FutureTask類實現兩者之間的聯系;如圖所示:
通過FutureTask類繼承結構可以發現它是Runnable接口的子類;
代碼實現如下:
package Java從入門到項目實戰.多線程編程.Java多線程實現; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; class CallableThread implements Callable<String> { @Override public String call() throws Exception { for (int i = 0; i < 10; i++) { System.out.println("線程執行 x = "+i); } return "xbhog"; } } public class Callable接口實現多線程 { public static void main(String[] args) throws ExecutionException, InterruptedException { //將Callable實例化包裝在FutureTask類中,這樣就可以與Runnable接口關聯 FutureTask<String> task = new FutureTask<String>(new CallableThread()); //線程啟動 new Thread(task).start(); //獲取call()的返回值 System.out.println("【線程返回數據】:"+task.get()); } }
感謝各位的閱讀,以上就是“多線程編程的三種實現方式是什么”的內容了,經過本文的學習后,相信大家對多線程編程的三種實現方式是什么這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。