java線程池

線程池

Java的線程實際映射到操作系統(tǒng)的線程,線程間上下文切換也是由操作系統(tǒng)完成的。如果一個進程頻繁的創(chuàng)建大量輕量級線程,那么用于線程創(chuàng)建和銷毀的時間跟線程真正運行的時間比起來將變得非常可觀,整個并發(fā)系統(tǒng)的效率變得低效。線程池是一種通過管理一些可重用線程來執(zhí)行并發(fā)任務(wù)的編程模式,其目的是降低線程創(chuàng)建和銷毀的資源開銷。線程池通常維護一個任務(wù)優(yōu)先隊列已經(jīng)一定數(shù)量的可重用線程,當(dāng)任務(wù)提交到線程池時,首先進入隊列,只有當(dāng)可重用線程有閑置位置時,才允許排隊任務(wù)占用一個可重用線程進行執(zhí)行。

[Thread Pool Demo Pic]

Java 中的 Executors, Executor 以及 ExecutorService

Executors是Java中創(chuàng)建和管理Executor已經(jīng)ExecutorService的工廠類和工具類,比如

    Executor executor = Executors.newSingleThreadExecutor();
    executor.execute(new Runnable() {
      @Override
      public void run() {
        System.out.println(Thread.currentThread().getName() + " is running ...");
      }
    });

Executor接口用于表示一個能執(zhí)行任務(wù)的類,該接口中只定義了一個接口方法

    /**
     * Executes the given command at some time in the future.  The command
     * may execute in a new thread, in a pooled thread, or in the calling
     * thread, at the discretion of the {@code Executor} implementation.
     *
     * @param command the runnable task
     * @throws RejectedExecutionException if this task cannot be
     * accepted for execution
     * @throws NullPointerException if command is null
     */
    void execute(Runnable command);

而ExecutorService則是用于表示一個能執(zhí)行任務(wù)并且能控制任務(wù)的執(zhí)行過程已經(jīng)結(jié)束的細節(jié)的接口,ExecutorService是一個Executor

public interface ExecutorService extends Executor {
  ...
  <T> Future<T> submit(Callable<T> task);
  ...
  Future<?> submit(Runnable task);
  ...
}

ExecutorService可以接受一個Callable,比如

    ExecutorService executorService = Executors.newFixedThreadPool(10);

    Future<String> future = executorService.submit(new Callable<String>() {
      @Override
      public String call() throws Exception {
        return "Hello World";
      }
    });

    String result = future.get();

Callable 跟 Runnable的區(qū)別在于,Callable可以有返回值以及可以拋出異常

public interface Callable<V> {
    /**
     * Computes a result, or throws an exception if unable to do so.
     *
     * @return computed result
     * @throws Exception if unable to compute a result
     */
    V call() throws Exception;
}

ExecutorService 的關(guān)閉

默認(rèn)情況下 ExecutorService 不會自動關(guān)閉,這會讓程序在JVM中保持運行狀態(tài)不會退出。shutdown() 方法使得 ExecutorService 停止接受新的任務(wù),在現(xiàn)有任務(wù)執(zhí)行完成后,ExecutorService 會自動退出。

executorService.shutdown();

而 shutdownNow() 方法讓ExecutorService立即退出,但對于正在運行的線程并不能保證正常退出;該方法還會返回所有等待執(zhí)行的任務(wù):

List<Runnable> notExecutedTasks = executorService.shutDownNow();

一種推薦的關(guān)閉 ExecutorService 的方法是,首先調(diào)用shutdown()方法停止接受新的任務(wù),然后等待一段時間,再調(diào)用shutdownNow()方法強制停止:

executorService.shutdown();
try {
    if (!executorService.awaitTermination(800, TimeUnit.MILLISECONDS)) {
        executorService.shutdownNow();
    } 
} catch (InterruptedException e) {
    executorService.shutdownNow();
}

ScheduledExecutorService

ScheduledExecutorService 接口提供的方法可以使任務(wù)在設(shè)定時間延時后啟動,也可以讓任務(wù)周期性的執(zhí)行。該接口提供如下一些方法:

  • schedule (Callable task, long delay, TimeUnit timeunit)
  • schedule (Runnable task, long delay, TimeUnit timeunit)
  • scheduleAtFixedRate (Runnable, long initialDelay, long period, TimeUnit timeunit)
  • scheduleWithFixedDelay (Runnable, long initialDelay, long period, TimeUnit timeunit)

例如

ScheduledExecutorService scheduledExecutorService =
        Executors.newScheduledThreadPool(5);

ScheduledFuture scheduledFuture =
    scheduledExecutorService.schedule(new Callable() {
        public Object call() throws Exception {
            System.out.println("Executed!");
            return "Called!";
        }
    },
    5,
    TimeUnit.SECONDS);

System.out.println("result = " + scheduledFuture.get());

scheduledExecutorService.shutdown();

創(chuàng)建了一個在5秒鐘過后執(zhí)行的任務(wù)。

scheduleAtFixedRate() 方法能創(chuàng)建一個周期性執(zhí)行的任務(wù),該任務(wù)會在初始延時 initialDelay 之后開始執(zhí)行,隨后每隔 period 周期執(zhí)行一次。如果任務(wù)在執(zhí)行過程中拋出異常,那么該任務(wù)不會再被周期執(zhí)行;如果任務(wù)執(zhí)行時間超過了周期間隔 period ,那么下一次執(zhí)行會等待當(dāng)期任務(wù)完成后立即開始,同一時間只會有一個任務(wù)在執(zhí)行。

scheduleWithFixedDelay() 跟 scheduleAtFixedRate() 相似,除了時間間隔 period 是以上一次任務(wù)執(zhí)行結(jié)束的時間開始算起,間隔 period 后再開始新的一個周期。

同樣,在不使用 ScheduledExecutorService 時,需要將其關(guān)閉,否則即便所有線程都結(jié)束了,ScheduledExecutorService 也不會退出。

Folk/Join 線程池

Folk/Join 線程池是 Java 7 引入的用于處理能遞歸的分解為子任務(wù)的多線程任務(wù);在多核CPU上有著性能的優(yōu)勢。詳見 Fork and Join: Java Can Excel at Painless Parallel Programming Too!

線程的sleep()方法和yield()方法有什么區(qū)別?
答:
① sleep()方法給其他線程運行機會時不考慮線程的優(yōu)先級,因此會給低優(yōu)先級的線程以運行的機會;yield()方法只會給相同優(yōu)先級或更高優(yōu)先級的線程以運行的機會;
② 線程執(zhí)行sleep()方法后轉(zhuǎn)入阻塞(blocked)狀態(tài),running-->blocked. 而執(zhí)行yield()方法后轉(zhuǎn)入就緒(ready)狀態(tài); running-->runnable
③ sleep()方法聲明拋出InterruptedException,而yield()方法沒有聲明任何異常;
④ sleep()方法比yield()方法(跟操作系統(tǒng)CPU調(diào)度相關(guān))具有更好的可移植性。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容