LeetCode-238-除自身以外陣列的乘積
學習java很久很久了,得有個5年了,但是從來都沒有真正的走進java世界,希望從這篇文章開始,把自己對java的點點滴滴都記錄下來。
從java5開始,java就提供了名叫Executor framework的機制,主要是圍繞著Executor介面, 它的介面 ExecutorService, 以及實現了這兩個介面的ThreadPoolExecutor類來展開,這種機制把執行緒的執行和建立分離開了,你只需要建立一個執行緒,然後把執行緒丟給Executor,讓它執行去吧。使用這個機制的另外一個好處是可以使用Callable介面,它類似於Runnable介面,但是有兩個不一樣的特性。
- 它的主要方法是call(), 它可以攜帶一個返回值。
- 當你傳送了一個Callable物件給executor之後,你可以拿到一個實現了Future介面的物件,通過這個物件,你可以控制物件的狀態以及Callable物件的結果。
public Server(){ executor=(ThreadPoolExecutor)Executors.newFixedThreadPool(5); System.out.printf("Server: Task Count: %dn",executor. getTaskCount()); } 提交帶返回值的任務。 public class FactorialCalculator implements Callable<Integer> { private Integer number; public FactorialCalculator(Integer number){ this.number=number; } @Override public Integer call() throws Exception { int result = 1; if ((num==0)||(num==1)) { result=1; } else { for (int i=2; i<=number; i++) { result*=i; TimeUnit.MILLISECONDS.sleep(20); } } System.out.printf("%s: %dn",Thread.currentThread(). getName(),result); return result; } public class Main { public static void main(String[] args) { ThreadPoolExecutor executor=(ThreadPoolExecutor)Executors. newFixedThreadPool(2); List<Future<Integer>> resultList=new ArrayList<>(); Random random=new Random(); for (int i=0; i<10; i++){ Integer number= random.nextInt(10); FactorialCalculator calculator=new FactorialCalculator(number); Future<Integer> result=executor.submit(calculator); resultList.add(result); } do { for (int i=0; i<resultList.size(); i++) { Future<Integer> result=resultList.get(i); System.out.printf("Main: Task %d: %sn",i,result. isDone()); } try { TimeUnit.MILLISECONDS.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } } while (executor.getCompletedTaskCount()<resultList.size()); System.out.printf("Main: Resultsn"); for (int i=0; i<resultList.size(); i++) { Future<Integer> result=resultList.get(i); Integer number=null; try { number=result.get(); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } System.out.printf("Main: Task %d: %dn",i,number); } executor.shutdown(); } } }
在Future物件裡面,我們可以通過result.isDone()方法來判斷執行緒是否計算完畢。
執行一堆任務,只返回第一個完成的任務。result = executor.invokeAny(taskList);
執行全部,resultList=executor.invokeAll(taskList);
推遲執行,executor.awaitTermination(1, TimeUnit.DAYS);
把任務的執行和結果的處理分開,需要用到CompletionService, CompletionServicei有兩個方法,take和poll,poll是如果沒有,它就會立刻返回一個null,take是沒有的話,會一直等待。
當執行緒池呼叫了關閉之後,它需要等待當前所有進行中的執行緒結束才會完全關閉,在這個過程當中提交的執行緒,需要拒絕處理,我們需要實現一個RejectedExecutionHandler,重寫它的rejectedExecution方法,然後聽過executor的setRejectedExecutionHandler()方法來設定。
public class RejectedTaskController implements
RejectedExecutionHandler {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor
executor) {
System.out.printf("RejectedTaskController: The task %s has
been rejectedn",r.toString());
System.out.printf("RejectedTaskController: %sn",executor.
toString());
System.out.printf("RejectedTaskController: Terminating:
%sn",executor.isTerminating());
System.out.printf("RejectedTaksController: Terminated:
%sn",executor.isTerminated());
}
Fork/Join Framework
Fork/Join Framwork的誕生是為了解決如下圖這樣的問題的
我們可以利用ForkJoinPool,它是一個特殊的Executors。
ForkJoin 框架主要是有兩個操作:
Fork:把一個任務分成幾個小任務,然後執行
Join:等待小任務的完成,並生成一個新任務。
這裡我把ForkJoin稱為刀叉框架,刀叉框架和Executor框架不一樣的地方在於work-stealing演算法,不同於Executor框架,刀叉框架在等待子任務的完成之前就已經建立並開始執行Join方法,Join方法一直在檢測任務是否完成並且開始執行。通過這樣的方式,可以很好的利用runtime的優勢,提高效能。
這樣就有一些限制:
任務只能採用Fork和Join來作為同步機制,而不能採用別的同步機制,如果採用其他的機制,他們在同步操作的時候,不能執行別的任務。比如:當你在刀叉框架裡面一個任務sleep的時候,別的正在執行的任務也會停止,直到該執行緒sleep結束。
刀叉框架的核心是兩個類:
(1)ForkJoinPool,它實現了ExecutorService介面和work-stealing演算法,通過它可以很好的管理正在執行的任務以及瞭解任務的資訊。
(2)ForkJoinTask,任務的基類,它提供了fork()方法和join()方法來控制任務的狀態。不同通常,你會實現他們的兩個子類,不帶返回結果的RecursiveAction和帶返回結果的RecursiveTask。