您好,登錄后才能下訂單哦!
本篇內容主要講解“何為Java多線程”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“何為Java多線程”吧!
什么是線程?
要說什么是「線程」,為了解釋這個概念,我首先要從「進程」講起。來看一下「進程」的概念:
?進程是指可執行程序存放在計算機存儲器的一個指令序列,它是一個動態執行的過程。?
看到這個概念后,是不是感覺完全懵了,那我們該怎么理解呢?我們來想象一下平時我們使用電腦的一個場景。
我們平時敲代碼的時候是不是邊聽音樂邊敲代碼邊用QQ跟女朋友聊天啊(反正我是除了最后一條,前兩天都占了,嗚嗚)。音樂播放器、代碼編輯器、QQ這三個軟件是同時運行的,這樣我們才能很多事情一起來完成,那么這三個軟件可以同時運行,就是「進程」在起作用。我們可以打開Windows的任務管理器,Windows的任務管理器中是可以看到有「進程」這么一個選項卡。
打開windows任務管理器后,我們就能看到當前操作系統中所運行的所有進程了。比如說上圖中我們看到的QQ的進程、Google瀏覽器的進程等等。而有的軟件可以有多個進程,比如一些殺毒軟件、數據庫的軟件等。
其實早期的操作系統都是單任務的操作系統,比如QQ,音樂播放器,它們只能單獨運行,一個運行之后,才能下一個運行,比如大家想象一下,你先聽歌曲,聽完歌曲之后你才能在QQ中回復好友的問題,是不是感覺特別不方便啊。
而我們現在的操作系統都是多任務的操作系統,可以多個程序同時運行,我們可以邊聽歌邊回復信息。,這就是我們的進程在起作用。
言歸正傳,我們現在說一下什么是線程:
?線程是比進程還要小的運行單位,一個進程包含多個線程。?
比如說一個程序是由很多行代碼組成的,那么這些代碼中就可以分成很多塊放到不同的線程中,去分別執行,所以我們認為,「線程相當于一個子程序」。
現在知道「進程」和「線程」的概念之后,問題就來了,我們知道,程序的運行是靠「CPU」來處理的,那如果你只有一個「CPU」的情況下,怎么能保證這些程序都能同時運行呢?這里面我們可以想象成把「CPU」的執行時間分成很多的小塊,每一小塊的時間都是固定的,我們可以把這個小塊叫「時間片」,時間片的時間可以非常短,比如說一毫秒,那么如果我們有音樂播放器、代碼編輯器、QQ三個軟件同時運行,那么它們三個如何去獲取CPU的執行時間呢?這個其實是隨機的,可以這樣考慮,我們的音樂播放器運行一毫秒,然后它會把CPU的使用權轉給代碼編輯器,代碼編輯器運行一毫秒將CPU的使用權轉給QQ,那么這些程序就輪流的在很短的時間內使用CPU,對于CPU來講,這些軟件其實是輪流運行的,但是由于它運行的時間間隔非常的短,作為我們使用者來說,是感覺不到它的變化的,這樣我們就會認為這些軟件都是同時運行的,這就是為什么在只有一個CPU的情況下,這些軟件能夠同時運行的原因,這個叫做時間片的輪轉。是通過對CPU的時間的輪轉來達到同時運行的效果的。
線程的創建
線程創建有兩種方式:
第一種:創建一個Thread類,或者一個Thread子類的對象。
第二種:創建一個實現Runnable接口的類的對象。
這里面涉及到了一個Thread類和一個Runnable接口,我們來了解一下這兩個系統為我們定義的類和接口都有哪些屬性和方法:
Thread類
Thread是一個線程類,位于java.lang包下
Thread類的常用方法
創建線程案例
通過繼承Thread類的方式創建線程類,重寫run()方法
?在這我提醒一下,對于很多初學者來說總有一個疑問,為什么它繼承Thread類就是一個線程了,這個我們說,Java中很多東西都是人家寫好了我們使用,Java中就是這么為大家規定的,你只有通過繼承Thread類、實現Runnable接口這兩種方式得到線程,所以我們按照這種方式做就可以了,這樣就能達到我們的目的。?
代碼演示
package com.thread; class MyThread extends Thread{ @Override public void run() { System.out.println("該線程正在執行!"); } } public class ThreadTest { public static void main(String[] args) { MyThread mt = new MyThread(); mt.start();//啟動線程 } }
運行結果
?不知道大家從上面代碼中看出來什么沒有,我來強調一下,啟動線程的時候,不是調用run()方法,我們以前學習的時候,執行哪部分內容就調用相對應的方法,而在線程中,使用start()方法去啟動線程,啟動線程、執行線程的時候執行的是run()方法里面的代碼,這個是需要我們注意的。?
?同時,還要注意一個線程只能執行一次,也就是只能調用一次start()方法?
package com.thread; class MyThread extends Thread{ @Override public void run() { System.out.println("該線程正在執行!"); } } public class ThreadTest { public static void main(String[] args) { MyThread mt = new MyThread(); mt.start();//啟動線程 mt.start(); } }
?如圖,如果多次調用start()方法,就會拋出異常。?
創建多個線程案例
package com.thread; class MyThread extends Thread{ public MyThread(String name){ super(name); } @Override public void run() { for (int i=0;i<=10;i++){ System.out.println(getName()+"正在運行"); } } } public class ThreadTest { public static void main(String[] args) { MyThread mt1 = new MyThread("線程一"); MyThread mt2 = new MyThread("線程二"); mt1.start();//啟動線程一 mt2.start();//啟動線程二 } }
運行結果
?在上面代碼中我創建了兩個線程,我們多運行幾次,會發現每次的運行結果都是不一樣的,從這里我們能體會到,線程要想去獲得CPU的使用權,其實是隨機的,其結果會出現很多種不同的情況。?
Runnable接口
只有一個方法run()
Runnable是Java中用以實現線程的接口
任何實現線程功能的類都必須實現該接口
創建線程案例
為什么要實現Runnable接口?
Java不支持多繼承
?如果你的Class類已經繼承了一個類,再去繼承Thread類是不可能的,所以這時候我們需要用接口去實現,因為接口可以同時實現多個接口?
不打算重寫Thread類的其他方法
?我們知道,我們繼承一個類,就會繼承這個類中的所有方法,但對于線程來說,其實我們只需要重寫run()方法就可以,如果不打算重寫Thread類中的其他方法,我們也可以用使用接口的方式。從我們的實際應用來看,使用實現Runnable接口的方式應用更廣泛一些。?
代碼演示
package com.thread; class PrintRunnable implements Runnable{ @Override public void run() { int i=1; while(i<=10){ System.out.println(Thread.currentThread().getName()+"正在運行"+(i++)); } } } public class RunnableTest { public static void main(String[] args) { PrintRunnable pr1 = new PrintRunnable(); Thread t1 = new Thread(pr1); t1.start(); PrintRunnable pr2 = new PrintRunnable(); Thread t2 = new Thread(pr2); t2.start(); } }
運行結果
?從上面的代碼中,我們發現,用實現Runnable接口的形式去創建接口的時候,我們是三步走,先定義Runnable實現類的對象,然后通過它創建線程類的對象,最后啟動線程,所以我們啟動線程只能通過Thread類以及它的子類去啟動。?
多個線程處理同一個資源的情況
package com.thread; class PrintRunnable implements Runnable{ int i=1; @Override public void run() { while(i<=10){ System.out.println(Thread.currentThread().getName()+"正在運行"+(i++)); } } } public class RunnableTest { public static void main(String[] args) { PrintRunnable pr = new PrintRunnable(); Thread t1 = new Thread(pr); t1.start(); Thread t2 = new Thread(pr); t2.start(); } }
運行結果
?出現上面這種運行結果,原因是run()方法被多個線程共享,多個線程也就是Thread類的實例,也就是pr對象被t1和t2兩個Thread類的實例共享,這適用于多個線程處理同一個資源的情。i相當于一個資源,t1和t2共享了這個資源。?
線程的狀態
新建(New)
可運行(Runnable)
正在運行(Running)
阻塞(Blocked)
終止(Dead)
?線程的狀態首先是「新建狀態」,創建一個Thread或者Thread子類的對象的時候,線程就進入了「新建狀態」。接下來是「可運行狀態」,當已經創建好的線程對象調用start()方法,這時候就進入了「可運行狀態」,在前面我也說過,線程什么時候運行是由CPU來決定的,只有當線程獲取CPU的使用權的時候,它才能執行。所以這里面,線程調用start()方法之后,不是馬上進入「運行狀態」,而是進入「可運行狀態」,這種狀態也叫「就緒狀態」,也就是我已經準備好了。接下來就是「正在運行狀態」,一個處于「可運行狀態」的線程,一旦獲取了CPU的使用權,就可以立即進入「正在運行狀態」,接下來就是「阻塞狀態」,當線程遇到一些干擾的時候,它將進入「阻塞狀態」,也就是它不再執行,后面我在說「生命周期」的時候也會詳細說「如何進入阻塞狀態」,最后是「終止狀態」,這就是線程的五個狀態。?
線程的生命周期
所謂的線程的「生命周期」,就是線程從「創建」到「啟動」,直至「運行結束」的這段時間我們就叫它的「生命周期」。其實線程的生命周期就是我上面說到的五個狀態相互的轉換過程,可以通過調用Thread類的一些相關方法來影響線程的狀態,「狀態之間的轉換」就可以構成我們最終的「生命周期」了。
首先是「新建狀態」,只要你創建了一個Thread或者Thread子類的對象,你就進入了「新建狀態」,然后通過調用線程的start()方法去啟動線程,進入「可運行狀態」,當處于「可運行狀態」的線程獲取CPU使用權后,它將進入「正在運行狀態」,如果一旦CPU的使用權到時間了,那么線程就會從「正在運行狀態」重新回到「可運行狀態」,去等待獲取下一次的CPU使用權,所以這塊從「正在運行狀態」到「可運行狀態」的一個條件就是「時間片」用完,還有一個就是調用yield()這樣一個方法,去從「運行狀態」變成「可運行狀態」。
那么再來看一下,「正在運行狀態」到「阻塞狀態」之間的轉換,可以通過調用Thread類中的一些方法來完成,比如說join()方法、wait()方法、sleep()方法,這些都可以把線程從「正在運行狀態」轉換成「阻塞狀態」,在后面我也會說到這些方法的使用。還有一種情況就是I/O請求,I是Input,O是Output,就是輸入輸出請求的意思。「阻塞狀態」我們可以看做一個正在運行的線程進入了一個暫停的狀態,I/O請求需要耗費一定的時間,這時候就可以讓線程進入「阻塞狀態」,等待I/O請求完成,再繼續進行執行。這是「正在運行狀態」到「阻塞狀態」的轉換,反過來,「阻塞狀態」的線程是不能直接轉換成「正在運行狀態」的,因為我之前說過要獲取CPU的使用權才能變成「正在運行狀態」。
所以這里「阻塞狀態」最終會轉換成「可運行狀態」,那么它要滿足什么條件呢?這個條件和之前說的「正在運行狀態」變成「阻塞狀態」的條件是對應的,也就是解決這些問題的方法。第一個是等待調用join()方法的線程執行完畢,第二個是調用notify()方法或notifyAll()方法,這兩個方法其實是對應wait()方法使用的,也就是調用wait()方法的線程必須調用notify()方法或notifyAll()方法才能進入「可運行狀態」,這個我會在后面說同步的時候詳細說。再有就是之前調用sleep()方法進行阻塞的線程,當你「休眠超時」以后,它就會重新變成「可運行狀態」。最后一點就是I/O請求完成,一旦I/O請求完成,那么線程依然可以回到「可運行狀態」。
最后,我們看到還有一個狀態是「終止狀態」,線程什么時候進入「終止狀態」呢?對于一個新建的線程來說,如果它去調用stop()方法,會進入「終止狀態」,同樣,對于一個「可運行狀態」的線程和一個處于「阻塞狀態」的線程,它們調用stop()方法也會進入「終止狀態」。在這我提一下,對于這個stop()方法,其實已經不建議使用了,因為Java的JDK當中會顯示「版本過期」,不推薦使用了。那么一個處在「正在運行狀態」的線程如何進入「終止狀態」呢?這里有幾種情況,第一種當然也是調用stop()方法,再有就是一個線程執行完畢了以后或者是一個正在執行的線程因為某些原因異常終止了,整個程序都終止了,它也會進入「終止狀態」,就跟一個人的生命一樣,從出生,然后長大,最后結束這樣的一個過程。
到此,相信大家對“何為Java多線程”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。