《Java並發編程實戰》(五)---- 任務執行

來源:互聯網
上載者:User

標籤:web伺服器   平台   有用   生命週期   並發   啟動   瞭解   問題   throws   

一,線上程中執行任務

1,無限建立線程的不足:

  • 線程生命週期的開銷非常高。線程的建立過程需要時間,這就延遲了請求的處理,並且需要JVM和作業系統提供一些輔助操作。
  • 資源消耗。如果可運行線程數量多於可用處理器的數量,那麼有些線程會閑置就會佔用許多記憶體,如果大量線程在競爭CPU還會產生其他的效能消耗。
  • 穩定性。在可建立線程的數量上有一個閾值,這個閾值隨著平台不同而不同,並且受多個因素制約,包括JVM的啟動參數、Thread建構函式中請求的棧大小,以及底層作業系統對線程的限制等。如果超過這個限制,就很可能有OOM異常。

在一定的範圍內,增加線程可以提高系統的輸送量,但如果超過這個範圍,再建立更多的線程只會降低程式的執行速度,如果過多地建立線程,整個系統就有可能崩潰。要想避免這種危險,就應該對應用程式可以建立的線程數量進行限制,並且全面地測試應用程式,從而確保線程數量達到限制時,程式也不會耗盡資源。

2,Executor架構

串列執行的問題在於其糟糕的響應性和輸送量,而“為每個任務都分配一個線程”的問題在於資源管理的複雜性。所以就有了線程池,線程池簡化了線程的管理工作,並且java.util.concurrent提供了一種靈活的線程池作為Executor架構的一部分。在java類庫中,任務執行的不是Thread,而是Executor:

public interface Executor {    void execute(Runnable command);}

1,基於Executor的Web伺服器

class TaskExecutorWebServer {    private static final int NTHREAD = 100;    private static final Executor exe = Executors.newFixedThreadPool(NTHREAD);    public static void main(String[] args) {        ServerSocket socket = new ServerSocket(80);        while(true) {            final Socket connection = socket.accept();            Runnable task = new Runnable() {                public void run() {                    handleRequest(connection);                }            };            exec.execute(task);        }    }}

在TaskExecutionWebServer中,通過使用Executor,將請求處理任務的提交與任務的實際執行解耦開來。

也可以很容易地將上例修改為ThreadPerTaskWebServer的行為,只需要使用一個為每個請求都建立新線程的Executor。

public class ThreadPerTaskExecutor implements Executor {    public void execute(Runnable r) {        new Thread(r).start();    }}

還可以編寫一個Executor使ThreadPerTaskExecutor的行為類似於單線程的行為:

public class WithinThreadExecutor implements Executor {    public void execute(Runnable r) {        r.run();    }}

***************************************************

每當看到下面形式的代碼時,並且希望獲得一種更靈活的執行策略時,考慮使用Executor來代替Thread:

new Thread(runnable).start();

***************************************************

3, 線程池

線程池是指管理一組相同背景工作執行緒的資源集區。線程池是與工作隊列密切相關的,其中在工作隊列中儲存了所有等待執行的任務。背景工作執行緒的任務很簡單: 從工作隊列中擷取一個任務,執行任務,然後返回線程池並等待下一個任務。

“線上程池中執行任務”比“為每個任務分配一個線程”優勢更多。通過重用現有的線程而不是建立新線程,可以減少線上程建立與銷毀的開銷。另一個好處是請求到來時,不會再因為要等待線程建立而延遲,也就提高了響應性。通過適當調整線程池的大小,可以建立足夠多的線程以便處理器保持忙碌狀態,同時還可以防止過多線程互相競爭資源而使應用程式耗盡記憶體。

類庫提供了一個靈活的線程池以及一些有用的預設配置。可以通過調用Executors中的靜態Factory 方法之一來建立一個線程池:

  • newFixedThreadPool:建立一個固定長度的線程池,每當提交一個任務時就建立一個線程,直到達到線程池的最大數量,這時線程池的規模將不再變化。
  • newCachedThreadPool:建立一個可快取的線程池,如果線程池的當前規模超過了處理需求時,那麼將回收閒置線程,而當需求增加時,則可以添加新的線程,線程池的規模不存在任何限制。
  • newSingleThreadExecutor:是一個單線程的Executor,它建立單個背景工作執行緒來執行任務,如果這個線程異常結束,會建立另一個線程來替代。它能確保依照任務在隊列中的順序來串列執行。
  • newScheduledThreadPool:建立了一個固定長度的線程池,而且以延遲或定時的方式執行任務,類似於Timer。

4,Executor的生命週期

由於Executor以非同步方式來執行任務,因此在任何時刻,之前提交任務的狀態不是立即可見的。有些任務可能已經完成,有些可能正在運行,而其他的任務可能在隊列中等待執行。當關閉應用程式時,可能採用平緩的方式(完成所有已經啟動的任務,並且不再接受任何新的任務),也可能採用粗暴方式(直接所有都關掉)。Executor視為應用程式提供服務的,因此它們也是可關閉的,並把在關閉操作中受影響的任務的狀態返回給應用程式。

為瞭解決執行任務的生命週期問題,ExecutorService介面擴充了Executor,添加了一些用於生命週期管理的方法:

public interface ExecutorService extends Executor {    void shutdown();    List<Runnable> shutDownNow();    boolean isShutdown();    boolean isTerminated();    boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException;    ...}

ExectuorService的生命週期有三種狀態:

《Java並發編程實戰》(五)---- 任務執行

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.