1. 程式人生 > 實用技巧 >配置單機實驗版hbase環境

配置單機實驗版hbase環境

執行緒池簡介

如果沒有使用執行緒池,會為每個請求都開一個新的執行緒,雖然理論上是可以的,但是會有缺點

  • 執行緒生命週期的開銷非常高。每個執行緒都有自己的生命週期,建立和銷燬執行緒所花費的時間和資源可能比處理客戶端的任務花費的時間和資源更多,並且還會有某些空閒執行緒也會佔用資源

  • 程式的穩定性和健壯性會下降,每個請求開一個執行緒。如果受到了惡意攻擊或者請求過多(記憶體不足),程式很容易就奔潰掉了。

所以說:我們的執行緒最好是交由執行緒池來管理,這樣可以減少對執行緒生命週期的管理,一定程度上提高效能。

執行緒池API

Excutor框架來使用執行緒池,它是執行緒池的基礎

  • Executor提供了一種將“任務提交”與“任務執行”

    分離開來的機制(解耦)

Callable和Future

執行緒池很多的API都有Callable和Future這兩個類

可以簡單認為:Callable就是Runnable的擴充套件

  • Runnable沒有返回值,不能丟擲受檢查的異常,而Callable可以

也就是說:當我們的任務需要返回值的時,我們就可以使用Callable

Future一般我們認為是Callable的返回值,但他其實代表的是任務的生命週期(當然了,它是能獲取得到Callable的返回值的)

public class CallableDemo implements Callable<Integer> {
    
private int num; public CallableDemo(int num){ this.num=num; } @Override public Integer call() throws Exception { int sum=0; for(int i=1;i<=num;i++){ sum=sum+i; } return sum; } ​ public static void main(String[] args) throws
ExecutionException, InterruptedException { // 建立執行緒池物件 ExecutorService service= Executors.newFixedThreadPool(2); // 可以執行Runnable物件或者Callable物件代表的執行緒 Future<Integer> f1=service.submit(new CallableDemo(10)); Future<Integer> f2=service.submit(new CallableDemo(20)); // V get() int i1=f1.get(); int i2=f2.get(); // 結束 service.shutdown(); System.out.println(i1); System.out.println(i2); } }

ThreadPoolExecutor詳解

ThreadPoolExecutor最終變數ctl定義為AtomicInteger記錄了“執行緒池中的任務數量”和“執行緒池的狀態”兩個資訊

執行緒的狀態:

  • RUNNING:執行緒池能夠接受新任務,以及對新新增的任務進行處理。

  • SHUTDOWN:執行緒池不可以接受新任務,但是可以對已新增的任務進行處理。

  • STOP:執行緒池不接收新任務,不處理已新增的任務,並且會中斷正在處理的任務

  • TIDYING:當所有的任務已終止,ctl記錄的"任務數量"為0,執行緒池會變為TIDYING狀態。當執行緒池變為TIDYING狀態時,會執行鉤子函式terminated()。terminated()在ThreadPoolExecutor類中是空的,若使用者想線上程池變為TIDYING時,進行相應的處理;可以通過過載terminated()函式來實現。

  • TERMINATED:執行緒池徹底終止的狀態

各個狀態之間轉換:

使用Executors建立執行緒池

一般使用Executors的工廠方法建立執行緒池,Executors三個比較常見的建立執行緒池方法:

  • newFixedThreadPool

    • 一個固定執行緒數的執行緒池,它將返回一個corePoolSize和maximumPoolSize相等的執行緒池

  • newCachedThreadPool

    • 非常有彈性的執行緒池,對於新的任務,如果此時執行緒池裡沒有空閒執行緒,執行緒池會毫不猶豫的建立一條新的執行緒去處理這個任務

  • SingleThreadExecutor

    • 單個worker執行緒的Executor

ThreadPoolExecutor構造方法

引數:

  • 指定核心執行緒數量

  • 指定最大執行緒數量

  • 允許執行緒空閒時間

  • 執行緒空閒時間的單位

  • 阻塞佇列

  • 執行緒工廠

  • 任務拒絕策略

引數要點:

執行緒數量要點

  • 如果執行執行緒的數量少於核心執行緒數量,則建立新的執行緒處理請求

  • 如果執行執行緒的數量大於核心執行緒數量,小於最大執行緒數量,則當佇列滿的時候才建立新的執行緒

  • 如果核心執行緒數量等於最大執行緒數量,那麼將建立固定大小的連線池

  • 如果設定了最大執行緒數量為無窮,那麼允許執行緒池適合任意的併發數量

執行緒空閒時間要點:

  • 當前執行緒數大於核心執行緒數,如果空閒時間已經超過了,那該執行緒會銷燬

排隊策略要點

  • 同步移交:不會放到佇列中,而是等待執行緒執行它。如果當前執行緒沒有執行,很可能會新開一個執行緒執行。

  • 無界限策略:如果核心執行緒都在工作,該執行緒會放到佇列中。所以執行緒數不會超過核心執行緒數

  • 有界限策略:可以避免資源耗盡,但是一定程度上減低了吞吐量

當執行緒關閉或者執行緒數量滿了和佇列飽和了,就有拒絕任務的情況了:

拒絕任務策略:

  • 直接丟擲異常

  • 使用呼叫者的執行緒來處理

  • 直接丟掉這個任務

  • 丟掉最老的任務

ThreadPoolExecutor執行方法execute

execute執行方法分了三步:

  • 如果執行緒池中執行的執行緒數量<corePoolSize,則建立新執行緒來處理請求,即使其他輔助執行緒是空閒的。

  • 如果執行緒池中執行的執行緒數量>=corePoolSize,且執行緒池處於RUNNING狀態,且把提交的任務成功放入阻塞佇列中,就再次檢查執行緒池的狀態,

    • 如果執行緒池不是RUNNING狀態,且成功從阻塞佇列中刪除任務,則該任務由當前 RejectedExecutionHandler 處理。

    • 否則如果執行緒池中執行的執行緒數量為0,則通過addWorker(null, false)嘗試新建一個執行緒,新建執行緒對應的任務為null。

ThreadPoolExecutor執行緒池關閉

ThreadPoolExecutor提供了shutdown()shutdownNow()兩個方法來關閉執行緒池

shutdown()

  • 按過去已提交任務的順序執行一個有序的關閉,不接受新任務

  • 中斷空閒執行緒

  • 將執行緒池狀態設定為TERMINATED

shutdownNow()

  • 嘗試停止所有的活動執行任務、暫停等待任務,並返回等待執行的任務列表

  • 正在執行的執行緒也會停止

區別

  • 呼叫shutdown()後,執行緒池狀態立刻變為SHUTDOWN,而呼叫shutdownNow(),執行緒池狀態立刻變為STOP

  • shutdown()等待任務執行完才中斷執行緒,而shutdownNow()不等任務執行完就中斷了執行緒。