從Java底層分析多線程行為

從Java底層分析多線程行為

從Java底層分析多線程行為

例如,處理大量信息的框架(如Spring批處理)使用線程來管理數據。同時操作線程或CPU進程可以提高性能,從而得到更快、更高效的程序。

從Java底層分析多線程行為

從Java底層分析多線程行為

從Java底層分析多線程行為


我們可以通過調用currentThread().getName()方法來訪問正在執行的線程,如下所示:

<code>public class MainThread {    public static void main(String[]args) {       System.out.println(Thread.currentThread().getName());    }}/<code>

此代碼將打印“main”,標識當前正在執行的線程。這是學習多線程概念的第一步。

從Java底層分析多線程行為

Java的線程生命週期包含六種線程狀態:

- New:Thread()已經實例化了一個新的。

- Runnable接:本Thread的start()方法被調用。

- Running:start()已調用該方法並且線程正在運行。

- Suspended:線程暫時掛起,可以由另一個線程恢復。

- Blocked:線程正在等待機會運行。當一個線程已經調用該synchronized()方法並且下一個線程必須等到它完成時,就會發生這種情況。

- Terminated:線程的執行完成。

從Java底層分析多線程行為

併發多線程處理:擴展Thread類

最簡單的是,通過擴展Thread類來完成併發處理,如下所示。

<code>public class InheritingThread extends Thread {    InheritingThread(StringthreadName) {        super(threadName);    }    public static voidmain(String... inheriting) {       System.out.println(Thread.currentThread().getName() + " isrunning");        newInheritingThread("inheritingThread").start();    }    @Override    public void run() {        System.out.println(Thread.currentThread().getName()+ " is running");    }}/<code>
從Java底層分析多線程行為

當我們start()使用new 調用方法時inheritingThread(),將run()執行方法中的邏輯。

我們還在Thread類構造函數中傳遞第二個線程的名稱,因此輸出將是:

<code>main is running.inheritingThread is running./<code>

併發多線程處理:Runnable接口

從Java底層分析多線程行為

Runnable在Thread構造函數內部傳遞會導致更少的耦合和更大的靈活性。傳遞之後Runnable,我們可以start()像上一個示例中那樣調用方法:

<code>public class RunnableThread implements Runnable {    public static voidmain(String... runnableThread) {       System.out.println(Thread.currentThread().getName());        new Thread(new RunnableThread()).start();    }    @Override    public void run() {       System.out.println(Thread.currentThread().getName());    }/<code>

非守護進程vs守護進程線程

在執行方面,有兩種類型的線程:

- 執行非守護程序線程會一直到結束。主線程本身就是非守護程序線程的一個很好的例子。main()除非System.exit()強制程序完成,否則代碼輸入將始終執行到最後。

- 一個守護線程是相反的,是一個不需要一直執行到結束的處理程序。

請記住規則:如果封閉的非守護程序線程在守護程序線程之前結束,則守護程序線程將在結束之前執行。為了更好地理解守護進程和非守護進程線程的關係,請參考以下示例

<code>import java.util.stream.IntStream;public class NonDaemonAndDaemonThread {    public static voidmain(String... nonDaemonAndDaemon) throws                        InterruptedException {       System.out.println("Starting the execution in the Thread "+      Thread.currentThread().getName());        Thread daemonThread = newThread(() ->     IntStream.rangeClosed(1, 100000)               .forEach(System.out::println));       daemonThread.setDaemon(true);        daemonThread.start();        Thread.sleep(10);        System.out.println("Endof the execution in the Thread " +                                             Thread.currentThread().getName());    }}/<code>

在這個例子中,我使用了守護程序線程來聲明1到100,000的範圍,迭代所有這些,然後打印。

從Java底層分析多線程行為

線程優先級和JVM

可以使用該setPriority方法確定線程執行的優先級,但是如何處理它取決於JVM實現。Linux,MacOS和Windows都有不同的JVM實現,每個都將根據自己的默認值處理線程優先級。

但是,你設置的線程優先級確實會影響線程調用的順序。在Thread類上的三個常數是:

<code> /**    * The minimum priority that athread can have.     */    public static final intMIN_PRIORITY = 1;   /**     * The default priority that isassigned to a thread.     */    public static final intNORM_PRIORITY = 5;    /**     * The maximum priority that athread can have.     */publicstaticfinalintMAX_PRIORITY=10;/<code>

嘗試對以下代碼運行一些測試,以查看最

<code>public class ThreadPriority {    public static voidmain(String... threadPriority) {        Thread moeThread = newThread(() -> System.out.println("Moe"));        Thread barneyThread = newThread(() -> System.out.println("Barney"));        Thread homerThread = newThread(() -> System.out.println("Homer"));       moeThread.setPriority(Thread.MAX_PRIORITY);       barneyThread.setPriority(Thread.NORM_PRIORITY);       homerThread.setPriority(Thread.MIN_PRIORITY);        homerThread.start();        barneyThread.start();        moeThread.start();    }}/<code>

即使我們設置moeThread為MAX_PRIORITY,我們也不能指望首先執行此線程。相反,執行順序將是隨機的。

但是,使用常量有一個問題:如果傳遞的優先級數字不在1到10的範圍內,setPriority()方法將引發IllegalArgumentException。所以我們可以使用枚舉來解決這個問題。使用枚舉Enums既簡化了代碼,又能夠更好地控制代碼的執行。

從Java底層分析多線程行為

<code>public class ThreadChallenge {    private static int line = 10;    public static voidmain(String... doYourBest) {        newtool("Car").start();        tool Bike = newtool("Bike");       Bike.setPriority(Thread.MAX_PRIORITY);        Bike.setDaemon(false);        Bike.start();        tool Train = newtool("Train");       Train.setPriority(Thread.MIN_PRIORITY);        Train.start();    }    static class tool extends Thread{        tool(String Name) {super(Name); }        @Override public void run(){            line++;            if (line == 13) {               System.out.println(this.getName());            }        }    }}/<code>
從Java底層分析多線程行為

從Java底層分析多線程行為

在上面的代碼中,我們創建了三個線程:

第一個線程是Car,我們為此線程分配了默認優先級。

第二個線程是Bike,分配了分配了MAX_PRIORITY優先級。

第三個線程是Train,分配了MIN_PRIORITY優先級。

然後我們開始了多線程。為了確定線程將運行的順序,你可能首先注意到tool類擴展了Thread類,並且我們已經在構造函數中傳遞了線程名稱。

我們還run()中如果line等於13就進行打印。

注意!即使Train是我們執行順序中的第三個線程,並且MIN_PRIORITY不能保證它將在所有JVM實現的最後執行。

你可能還會注意到,在此示例中,我們將Bike線程設置為守護,所以它是一個守護程序線程,Bike可能永遠不會完成執行。但是其他兩個線程默認是非守護進程,因此Car和Train線程肯定會完成它們的執行。

總之,結果將是D:不確定,因為無法保證線程調度程序將遵循我們的執行順序或線程優先級。請記住,如果不借助JUC的工具,我們不能依賴程序邏輯(線程或線程優先級的順序)來預測線程的執行順序。

從Java底層分析多線程行為

從Java底層分析多線程行為

多線程常見錯誤

- 調用run()方法以嘗試啟動新線程。

- 試圖啟動一個線程兩次(這將導致一個IllegalThreadStateException)。

- 允許多個進程在不應更改時更改對象的狀態。

- 編寫依賴於線程優先級的程序邏輯

- 依賴於線程執行的順序 - 即使我們首先啟動一個線程,也不能保證它將首先被執行。

關於多線程要記住什麼

- 調用start()方法啟動一個 Thread。

- 可以Thread直接擴展類以使用線程。

- 可以在Runnable接口內實現線程動作。

- 線程優先級取決於JVM實現。

- 線程行為將始終取決於JVM實現。

- 如果封閉的非守護程序線程首先結束,則守護程序線程將無法完成。

從Java底層分析多線程行為

小編是一個有著5年工作經驗的程序員,關於Java,自己有做材料的整合,一個完整學習Java的路線,學習材料和工具。需要的夥伴可以私信我,發送“交流”後就可免費獲取。對於學習Java有任何問題(學習方法,學習效率,如何就業)都可以問我。希望你也能憑自己的努力,成為下一個優秀的程序員!


分享到:


相關文章: