線程池啟動線程的方式:java啟動線程的方法

我們在學習軟件開發時,多線程,高並發是一個必不可少的知識點,也是在面試時必會問到的內容,為了讓大家對多線程,高並發編程有個清晰認識,特地組織了一個專欄來專門介紹一下,希望能對大家有一些幫助。

線程簡介

線程是程序運行的基本執行單元。當操作系統(不包括單線程的操作系統,如微軟早期的DOS)在執行一個程序時,會在系統中建立一個進程,而在這個進程中,必須至少建立一個線程(這個線程被稱為主線程)來作為這個程序運行的入口點。因此,在操作系統中運行的任何程序都至少有一個主線程。

進程和線程是現代操作系統中兩個必不可少的運行模型。在操作系統中可以有多個進程,這些進程包括系統進程(由操作系統內部建立的進程)和用戶進程(由用戶程序建立的進程);一個進程中可以有一個或多個線程。進程和進程之間不共享內存,也就是說系統中的進程是在各自獨立的內存空間中運行的。而一個進程中的線可以共享系統分派給這個進程的內存空間。

在進一步介紹之前,我們先來了解一些基本概念,以幫助大家更快速的理解。

基本概念

進程

進程是操作系統中正在執行的不同的應用程序,例如:我們可以同時打開微信和QQ,甚至更多的程序。

在操作系統中運行的程序就是進程,進程就是執行程序的一次執行過程,它是一個動態的概念式系統資源分配的單位

通常在一個進程中可以包含若干個線程,當然一個進程中至少有一個線程,不然沒有存在的意義,線程是CPU調度和執行的單位

線程

線程是一個應用程序進程中不同的執行路徑,例如:我們的WEB服務器,能夠為多個用戶同時提供請求服務。

進程是不活潑的,進程從來不執行任何東西,它只是線程的容器。線程總是在某個進程環境中創建的,而且它的整個生命周期都在該進程中。

在一個進程中,如果創建了多個線程,線程的運行是由調度器安排調度的,調度器是與操作系統緊密相關的,先後順序是不能人為干預的

多線程

多線程擁有多條執行路徑,「主線程與子線程並行交替執行」(普通方法只有主線程一條路徑),對同一份資源操作時,會存在資源搶奪的問題,這時就需要加入並發控制了。

一個Java應用程序,至少有三個線程: main()主線程, gc()垃圾回收線程,異常處理線程。當然如果發生異常,會影響主線程。

多線程程序的優點:

  • 提高應用程序的響應。對圖形化界面更有意義,可增強用戶體驗。同時做多個事情。比如:一邊聽歌、一邊寫代碼。
  • 提高計算機系統CPU的利用率。不過線程也會帶來額外的開銷,如CPU調度時間,並發控制帶來的系統開銷。
  • 改善程序結構。將既長又複雜的進程分為多個線程,獨立運行,利於理解和修改。

何時需要多線程?

程序需要同時執行兩個或多個任務。

需要一些後台運行的程序時,比如:Java後台運行的GC線程。

創建線程

Java中創建線程有四種方式,我們下面依次介紹一下。

1、繼承 Thread 類

(1)定義Thread類的子類,並重寫該類的run方法,該run方法的方法體就代表了線程要完成的任務。因此把run()方法稱為執行體。

(2)創建Thread子類的實例對象,即創建了一個線程對象。

(3)調用該線程對象的start()方法來啟動該線程。

示例代碼:

public class MyThread extends Thread { 
 
    // 總票數 
    public int count = 10; 
 
    @Override 
    public void run() { 
        // 當還有票時就繼續售賣 
        while (count > 0) { 
            // 剩餘票數 
            count--; 
            System.out.println( 
                    Thread.currentThread().getName() + "售賣第 " + (10 - count) + " 張票,當前剩餘票數: " + count); 
        } 
    } 
 
    public static void main(String[] args) { 
        MyThread myThread = new MyThread(); 
        myThread.start(); 
    } 
} 

2、實現Runnable接口

(1)定義Runnable接口的實現類,並重寫該接口的run()方法,該run()方法的方法體同樣是該線程的線程執行體。

(2)創建 Runnable實現類的實例對象,並將該實例作為Thread的target來創建一個Thread對象,該Thread對象才是真正的線程運行對象。

(3)調用該線程對象的start()方法來啟動線程。

示例代碼:

public class MyRunableThread implements Runnable { 
 
    // 總票數 
    public int count = 10; 
 
    @Override 
    public void run() { 
        // 當還有票時就繼續售賣 
        while (count > 0) { 
            // 剩餘票數 
            count--; 
            System.out.println( 
                    Thread.currentThread().getName() + "售賣第 " + (10 - count) + " 張票,當前剩餘票數: " + count); 
        } 
    } 
 
    public static void main(String[] args) { 
        MyRunableThread myRunableThread = new MyRunableThread(); 
        Thread myThread = new Thread(myRunableThread); 
        myThread.start(); 
    } 
} 

「Thread 和 Runnable 的區別」

上述兩種方法是大家最常見到的兩種創建線程的方法,也常常會被問到兩種方式創建線程的區別,下面簡單總結了一下:

「繼承 Thread 類」

子類繼承 Thread 類具備多線程能力

啟動線程:子類線程對象調用 .start()方法

不建議使用:避免 OOP 單繼承局限性

「實現接口 Runnable」

  • 具有多線程能力
  • 啟動線程:傳入目標對象 + Thread對象調用.start()方法
  • 推薦使用:避免單繼承局限性,方便同一個對象被多個線程使用

另外,在使用線程池時只能放入實現Runable或Callable類線程,不能直接放入繼承Thread的類

3、實現Callable接口

(1)創建Callable接口的實現類,並實現call()方法,該call()方法將作為線程執行體,並且有返回值。

(2)創建Callable實現類的實例,使用FutureTask類來包裝Callable對象,該FutureTask對象封裝了該Callable對象的call()方法的返回值。

(3)使用FutureTask對象作為Thread對象的target創建並啟動新線程。

(4)調用FutureTask對象的get()方法來獲得子線程執行結束後的返回值。

示例代碼:

public class MyCallableThread implements Callable<String> { 
 
    // 總票數 
    private int count = 10; 
 
    @Override 
    public String call() throws Exception { 
        // 當還有票時就繼續售賣 
        while (count > 0) { 
            // 剩餘票數 
            count--; 
            System.out.println( 
                    Thread.currentThread().getName() + "售賣第 " + (10 - count) + " 張票,當前剩餘票數: " + count); 
        } 
        return "票已售完"; 
    } 
 
    public static void main(String[] args) throws InterruptedException, ExecutionException { 
        Callable<String> callable = new MyCallableThread(); 
        FutureTask<String> futureTask = new FutureTask<>(callable); 
        Thread myThread = new Thread(futureTask); 
        myThread.start(); 
        // 打印返回結果 
        System.out.println(futureTask.get()); 
    } 
} 

「Runnable和Callable的區別:」

Callable規定的方法是call(),Runnable規定的方法是run()。

Callable的任務執行後可返回值,而Runnable的任務是不能有返回值。

call方法可以拋出異常,run方法不可以。

4、線程池

Java默認提供了五種線程池,通過Executors創建,分別為:

  • 「newCachedThreadPool」 創建一個可緩存線程池,如果線程池長度超過處理需要,可靈活回收空閑線程,若無可回收,則新建線程。
  • 「newFixedThreadPool」 創建一個定長線程池,可控制線程最大並發數,超出的線程會在隊列中等待。
  • 「newScheduledThreadPool」 創建一個定長線程池,支持定時及周期性任務執行。
  • 「newSingleThreadExecutor」 創建一個單線程化的線程池,它只會用唯一的工作線程來執行任務,保證所有任務按照指定順序(FIFO, LIFO, 優先級)執行。
  • 「newWorkStealingPool」 創建一個具有搶佔式操作的線程池,由於能夠合理的使用CPU進行對任務操作(並行操作),所以適合使用在很耗時的任務中。

示例代碼:

public class MyThreadPool implements Runnable { 
 
    // 總票數 
    public int count = 10; 
 
    @Override 
    public void run() { 
        // 當還有票時就繼續售賣 
        while (count > 0) { 
            // 剩餘票數 
            count--; 
            System.out.println( 
                    Thread.currentThread().getName() + "售賣第 " + (10 - count) + " 張票,當前剩餘票數: " + count); 
        } 
    } 
 
    public static void main(String[] args) { 
        ExecutorService ex = Executors.newFixedThreadPool(5); 
        MyThreadPool t = new MyThreadPool(); 
        ex.submit(t); 
        ex.shutdown(); 
    } 
} 

關於線程池,後續會有單獨文章給大家詳細介紹。

通過以上的內容,希望大家可以對線程有個初步的認識,相關示例代碼,稍後整理後我會上傳到GitHub上,也請大家留意我們的後續文章。

原創文章,作者:投稿專員,如若轉載,請註明出處:https://www.506064.com/zh-hant/n/221521.html

(0)
打賞 微信掃一掃 微信掃一掃 支付寶掃一掃 支付寶掃一掃
投稿專員的頭像投稿專員
上一篇 2024-12-09 13:11
下一篇 2024-12-09 13:11

相關推薦

發表回復

登錄後才能評論