Java線程&線程池

Thread

Java中的Thread是用于實現多線程編程的類。通過創建Thread對象并調用其start()方法,可以啟動一個新的線程并執行指定的代碼。

Thread使用

以下是一個簡單的示例,展示了如何使用Thread類創建和啟動一個線程:

public class MyThread extends Thread {
    public void run() {
        // 線程要執行的代碼
        System.out.println("Hello from a thread!");
    }

    public static void main(String[] args) {
        MyThread thread = new MyThread();
        thread.start(); // 啟動線程
    }
}

在上面的例子中,我們創建了一個繼承自Thread類的自定義線程類MyThread。在run()方法中定義了線程要執行的代碼,這里只是簡單地打印一條消息。在main()方法中,我們創建了一個MyThread對象,并調用其start()方法來啟動線程。

啟動線程后,它會在后臺執行run()方法中的代碼。注意,不要直接調用run()方法,而是使用start()方法來啟動線程,這樣才能實現多線程并發執行的效果。

需要注意的是,Java中還有其他實現多線程的方式,比如實現Runnable接口、使用線程池等。但Thread類是最基本和常用的多線程編程方式之一。

Thread的一些缺點包括:

  1. 多線程編程復雜性:使用多線程編程需要考慮線程同步、死鎖、競態條件等問題,這增加了程序設計和調試的復雜性。

  2. 內存消耗:每個線程都需要一定的內存來維護線程棧、程序計數器等信息,當線程數量過多時,會增加內存消耗。

  3. 上下文切換開銷:線程之間的切換需要保存和恢復上下文信息,這會帶來一定的開銷。

  4. 線程安全問題:多線程環境下,共享資源的訪問需要進行同步,否則可能會出現數據競爭和不一致的問題。

  5. 難以調試:多線程程序的調試相對復雜,由于線程的并發執行,問題的復現和定位可能會更加困難。

  6. 可能導致死鎖:如果線程之間存在循環等待資源的情況,就可能導致死鎖,使得程序無法繼續執行。

總之,雖然Java Thread提供了多線程編程的便利性,但也存在一些缺點,需要開發人員在設計和實現時注意避免潛在的問題。

線程池(ExecutorService、ThreadPool)

newCachedThreadPool

newCachedThreadPool是Java中的一個線程池創建方法,它返回一個ThreadPoolExecutor對象,該對象可以用于執行多個任務。它是一個可緩存的線程池,可以根據需要創建新的線程,如果線程池中的線程空閑時間超過指定的時間(默認為60秒),則會被終止并移除。當任務到達時,如果線程池中有空閑線程,則會立即執行任務;如果沒有空閑線程,則會創建新的線程來執行任務。適用于執行大量的短期異步任務的場景,不適用于長期運行的任務。

使用示例:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class CachedThreadPoolExample {
    public static void main(String[] args) {
        // 創建一個可緩存的線程池
        ExecutorService executorService = Executors.newCachedThreadPool();

        // 提交任務給線程池執行
        for (int i = 0; i < 10; i++) {
            final int taskId = i;
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println("Task " + taskId + " is being executed.");
                }
            });
        }

        // 關閉線程池
        executorService.shutdown();
    }
}

在上面的示例中,我們首先使用Executors.newCachedThreadPool()方法創建一個可緩存的線程池。然后,我們使用execute()方法向線程池提交了10個任務,每個任務都是一個Runnable對象,用于打印任務的編號。最后,我們調用shutdown()方法關閉線程池。

newFixedThreadPool

newFixedThreadPool 是Java中的一個線程池創建方法。它創建一個固定大小的線程池,其中線程的數量是固定的,不會根據任務的數量進行動態調整。

使用 newFixedThreadPool 方法創建的線程池可以同時執行指定數量的任務,當有新的任務提交時,如果線程池中有空閑的線程,則會立即執行;如果線程池中沒有空閑的線程,則新的任務會被放入任務隊列中等待執行,直到有線程空閑為止。

這種線程池適用于需要控制并發線程數量的場景,可以有效地控制系統資源的使用,避免因為線程過多而導致系統負載過高的問題。

使用示例:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadPoolExample {
    public static void main(String[] args) {
        // 創建一個固定大小為5的線程池
        ExecutorService executor = Executors.newFixedThreadPool(5);

        // 提交任務給線程池
        for (int i = 0; i < 10; i++) {
            final int taskNum = i;
            executor.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println("正在執行任務 " + taskNum);
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("任務 " + taskNum + " 執行完畢");
                }
            });
        }

        // 關閉線程池
        executor.shutdown();
    }
}

上述示例中,我們首先通過Executors.newFixedThreadPool(5)創建了一個固定大小為5的線程池。然后,我們通過executor.execute()方法提交了10個任務給線程池進行執行。每個任務都會打印出自己的任務編號,并在執行完畢后打印出執行完畢的消息。最后,我們通過executor.shutdown()方法關閉線程池。

使用線程池可以更好地管理和控制線程的執行,提高程序的性能和效率。newFixedThreadPool方法創建的線程池具有固定大小,適用于需要控制并發線程數量的場景。

newScheduledThreadPool

newScheduledThreadPool是Java中的一個方法,用于創建一個可調度的線程池。它返回一個ScheduledExecutorService對象,可以用來執行定時任務和周期性任務。

使用示例:

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class Main {
    public static void main(String[] args) {
        ScheduledExecutorService executor = Executors.newScheduledThreadPool(5);
        
        // 執行定時任務
        executor.schedule(() -> {
            System.out.println("定時任務執行");
        }, 1, TimeUnit.SECONDS);
        
        // 執行周期性任務
        executor.scheduleAtFixedRate(() -> {
            System.out.println("周期性任務執行");
        }, 0, 2, TimeUnit.SECONDS);
        
        // 關閉線程池
        executor.shutdown();
    }
}

在上述示例中,我們通過Executors.newScheduledThreadPool(5)創建了一個可調度的線程池,其中參數5表示線程池的大小為5。然后,我們可以使用返回的ScheduledExecutorService對象執行定時任務和周期性任務。最后,通過調用executor.shutdown()方法來關閉線程池。

newSingleThreadExecutor

newSingleThreadExecutor 是 Java 中的一個線程池創建方法,用于創建一個只有一個線程的線程池。它的定義如下:

ExecutorService executor = Executors.newSingleThreadExecutor();

這個線程池只會創建一個線程來執行任務,當這個線程執行完一個任務后,會接著執行下一個任務。如果有多個任務提交給這個線程池,它們會按照提交的順序依次執行。

使用 newSingleThreadExecutor 可以保證任務按照順序執行,適用于需要順序執行的場景,比如需要按照任務的提交順序來處理數據的情況。

示例代碼:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class SingleThreadExecutorExample {
    public static void main(String[] args) {
        // 創建一個單線程的線程池
        ExecutorService executor = Executors.newSingleThreadExecutor();

        // 提交任務給線程池執行
        executor.submit(() -> {
            System.out.println("Task 1 is running");
        });

        executor.submit(() -> {
            System.out.println("Task 2 is running");
        });

        // 關閉線程池
        executor.shutdown();
    }
}

在上面的示例中,我們首先使用Executors.newSingleThreadExecutor()方法創建了一個單線程的線程池。然后,我們使用executor.submit()方法提交了兩個任務給線程池執行。最后,我們調用executor.shutdown()方法關閉線程池。

由于newSingleThreadExecutor創建的是一個單線程的線程池,所以任務會按照提交的順序依次執行。這樣可以保證任務之間的順序性,適用于需要按照順序執行的場景。

Java提供的四種線程池的優點

Java提供了四種線程池,分別是FixedThreadPool、CachedThreadPool、ScheduledThreadPool和SingleThreadPool。它們各自有不同的優點:

  1. FixedThreadPool:固定大小的線程池。它適用于需要控制并發線程數的場景,可以限制線程的數量,避免資源耗盡。適用于執行長期的任務,效率高。

  2. CachedThreadPool:可緩存的線程池。它適用于執行大量的短期任務,可以根據需要自動創建新的線程,線程池的大小可以根據任務的多少自動調整。適用于執行大量的耗時較短的任務,靈活性高。

  3. ScheduledThreadPool:定時任務線程池。它適用于需要定時執行任務的場景,可以按照指定的時間間隔周期性地執行任務。適用于需要定時執行任務的場景,如定時備份、定時統計等。

  4. SingleThreadPool:單線程池。它適用于需要保證任務按照順序執行的場景,所有任務都在同一個線程中按照指定的順序執行。適用于需要保證任務按照順序執行的場景,如消息隊列等。

這四種線程池都是通過線程池Executor框架提供的,可以有效地管理和復用線程,提高程序的性能和效率。

自定義線程池

自定義線程池步驟:

  1. 創建一個線程池類,可以命名為CustomThreadPool
  2. 在該類中,需要定義以下屬性:
    • ThreadPoolExecutor對象:用于管理線程池的執行和調度。
    • int類型的corePoolSize:指定線程池的核心線程數,即線程池中保持活動狀態的線程數。
    • int類型的maximumPoolSize:指定線程池的最大線程數,即線程池中允許的最大線程數。
    • long類型的keepAliveTime:指定線程池中非核心線程的閑置超時時間。
    • BlockingQueue<Runnable>類型的workQueue:用于存放待執行的任務的阻塞隊列。
  3. CustomThreadPool類的構造方法中,初始化線程池對象,并設置相關屬性。
  4. 提供方法來提交任務到線程池中執行,可以命名為submitTask方法。該方法接受一個Runnable類型的任務作為參數,并將任務提交到線程池中執行。
  5. 可以根據需要,提供其他方法來管理線程池,如獲取當前活動線程數、獲取線程池中的任務數量等。

示例代碼:

import java.util.concurrent.*;

public class CustomThreadPool {
    private ThreadPoolExecutor threadPool;

    public CustomThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, BlockingQueue<Runnable> workQueue) {
        threadPool = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit.MILLISECONDS, workQueue);
    }

    public void submitTask(Runnable task) {
        threadPool.submit(task);
    }

    public int getActiveThreadCount() {
        return threadPool.getActiveCount();
    }

    public int getTaskCount() {
        return threadPool.getQueue().size();
    }
}

使用自定義線程池的示例代碼:

public class Main {
    public static void main(String[] args) {
        // 創建一個阻塞隊列作為任務隊列
        BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(10);

        // 創建自定義線程池對象
        CustomThreadPool threadPool = new CustomThreadPool(5, 10, 5000, workQueue);

        // 提交任務到線程池中執行
        for (int i = 0; i < 20; i++) {
            final int taskNum = i;
            threadPool.submitTask(() -> {
                System.out.println("Task " + taskNum + " is running.");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("Task " + taskNum + " is finished.");
            });
        }

        // 輸出線程池中的活動線程數和任務數量
        System.out.println("Active Thread Count: " + threadPool.getActiveThreadCount());
        System.out.println("Task Count: " + threadPool.getTaskCount());

        // 關閉線程池
        threadPool.shutdown();
    }
}

以上代碼示例中,創建了一個自定義線程池對象threadPool,并提交了20個任務到線程池中執行。然后通過getActiveThreadCount方法和getTaskCount方法獲取線程池中的活動線程數和任務數量。最后調用shutdown方法關閉線程池。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容