Fork/Join
在JDK1.7版本中提供了Fork/Join 並行執行任務框架,它的主要作用是把大任務分割成若干個小任務,再對每個小任務得到的結果進行彙總,此種開發方法也叫分治編程,分治編程可以極大地利用CPU資源,提高任務執行的效率,也是目前與多線程有關的前沿技術。
Fork-Join分治編程與類結構
在JDK中並行執行框架Fork-Join使用了“工作竊取( work- stealing)”
算法,它是指某個線程從其他隊列裏竊取任務來執行,那這樣做有什麼優勢或者目的是什麼呢?
比如要完成一個 比較大的任務,完全可以把這個大的任務分割爲若千互不依賴的子任務/小任務,爲了更加方便地管理這些任務,於是把這些子任務分別放到不同的隊列裏,這時就會出現有的線程會先把自己隊列裏的任務快速執行完畢,而其他線程對應的隊列裏還有任務等待處理,完成任務的線程與其等着,不如去幫助其他線程分擔要執行的任務,於是它就去其他線程的隊列裏竊取一個任務來執行,這就是所謂的“工作竊取(work-stealing)” 算法。
在JDK.7中實現中實現分治編程需要使用ForkJoinPool類,此類的主要作用是創建一個任務池,類信息如下:
public class ForkJoinPoll extends AbstractExecutorService{}
該類也是從AbstractExxcutorService類繼承下來的。
類ForkJoinPool所提供的功能是一個任務池,而執行任務卻不是ForkJoinPool,而是ForkJoinTask類。
類FokJoinTask是抽象類,不能實例化,所以需要該類的3個子類CountedCompleter、RecursiveAction和RecursiveTask
來實現具體功能。
使用RecursiveAction讓任務跑起來
使用類RecursiveAction執行的任務是具有無返回值的,僅執行一次任務
。
public class MyTestA {
public static void main(String[] args) throws InterruptedException {
ForkJoinPool poll = new ForkJoinPool();
poll.submit(new MyRecursiveAction());
Thread.sleep(5000);
}
}
class MyRecursiveAction extends RecursiveAction {
@Override
protected void compute() {
System.out.println("compute run!");
}
}
使用RecursiveAction分解任務
前面的示例僅是讓任務運行起來,並打印一個字符串信息,任務並沒有得到fork分解,也就是並沒有體現分治編程的運行效果。在調用ForkJoinTask.java類中的fork() 方法時需要注意一下效率的問題,因爲每一次調用fork 都會分離任務,增加系統運行負擔,所以在ForkJoinTask.java類中提供了public static void invokeAll(ForkJoinTask<?> tl, ForkJoinTask<?>t2)
方法來優化執行效率。
public class MyTestA {
public static void main(String[] args) throws InterruptedException {
ForkJoinPool poll = new ForkJoinPool();
poll.submit(new MyRecursiveAction(1, 10));
Thread.sleep(5000);
}
}
class MyRecursiveAction extends RecursiveAction {
private int beginValue;
private int endValue;
public MyRecursiveAction(int beginValue, int endValue) {
this.beginValue = beginValue;
this.endValue = endValue;
}
@Override
protected void compute() {
System.out.println(Thread.currentThread().getName() + "------");
if (endValue - beginValue > 2) {
int middleNum = (beginValue + endValue) / 2;
MyRecursiveAction leftAction = new MyRecursiveAction(beginValue, middleNum);
MyRecursiveAction rightAction = new MyRecursiveAction(middleNum + 1, endValue);
invokeAll(leftAction, rightAction);
} else {
System.out.println("打印組合爲:" + beginValue + "-" + endValue);
}
}
}
任務被成功分解。
ForkJoinPool-1-worker-9------
ForkJoinPool-1-worker-9------
ForkJoinPool-1-worker-9------
打印組合爲:1-3
ForkJoinPool-1-worker-2------
ForkJoinPool-1-worker-11------
ForkJoinPool-1-worker-4------
ForkJoinPool-1-worker-2------
打印組合爲:4-5
打印組合爲:6-8
打印組合爲:9-10
使用RecursiveTask取得返回值與join()和get()方法的區別
使用類RecursiveTask執行的任務具有返回值的功能。
public class MyTestB {
public static void main(String[] args) {
try {
MyResursiveTask task1 = new MyResursiveTask();
System.out.println(task1.hashCode());
ForkJoinPool pool = new ForkJoinPool();
ForkJoinTask task2 = pool.submit(task1);
System.out.println(task2.hashCode() + " " + task2.get());
Thread.sleep(5000);
} catch (Exception e) {
e.printStackTrace();
}
}
}
class MyResursiveTask extends RecursiveTask<Integer> {
@Override
protected Integer compute() {
System.out.println("compute time=" + System.currentTimeMillis());
return 100;
}
}
可以使用join()方法來取得結果值:
public static void main(String[] args) {
try {
MyResursiveTask task1 = new MyResursiveTask();
System.out.println(task1.hashCode());
ForkJoinPool pool = new ForkJoinPool();
ForkJoinTask<Integer> task2 = pool.submit(task1);
System.out.println(task2.hashCode() + " " + task2.join());
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
方法join()與get()雖然都能取得計算後的結果值,但它們之間還是在出現異常時有處理上的區別:
public class MyTestC {
public static void main(String[] args) {
try {
MyRecursiveTaskA taskA = new MyRecursiveTaskA();
ForkJoinPool pool = new ForkJoinPool();
ForkJoinTask<Integer> task = pool.submit(taskA);
System.out.println(task.get());
for (int i = 0; i < Integer.MAX_VALUE; i++) {
String newString = new String();
Math.random();
Math.random();
Math.random();
Math.random();
Math.random();
Math.random();
Math.random();
}
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("main end");
}
}
class MyRecursiveTaskA extends RecursiveTask<Integer> {
@Override
protected Integer compute() {
System.out.println(Thread.currentThread().getName() + " 執行了compute方法");
String nullString = null;
nullString.toString();//NullPoint
return 100;
}
}
使用get()方法執行任務時,當子任務出現異常時可以在main主線程中進行捕獲。
使用RecursiveTask執行多個任務並打印返回值
public class MyTestD {
public static void main(String[] args) throws InterruptedException {
ForkJoinPool pool = new ForkJoinPool();
ForkJoinTask<Integer> taskA = pool.submit(new AMyRecursiveTask());
ForkJoinTask<Integer> taskB = pool.submit(new BMyRecursiveTask());
System.out.println("準備打印:" + System.currentTimeMillis());
System.out.println(taskA.join() + " A: " + System.currentTimeMillis());
System.out.println(taskB.join() + " B: " + System.currentTimeMillis());
Thread.sleep(5000);
}
}
class AMyRecursiveTask extends RecursiveTask<Integer> {
@Override
protected Integer compute() {
try {
System.out.println(Thread.currentThread().getName() + " begin A" + System.currentTimeMillis());
Thread.sleep(3000);
System.out.println(Thread.currentThread().getName() + " end B" + System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
return 100;
}
}
class BMyRecursiveTask extends RecursiveTask<Integer> {
@Override
protected Integer compute() {
try {
System.out.println(Thread.currentThread().getName() + " begin B" + System.currentTimeMillis());
Thread.sleep(5000);
System.out.println(Thread.currentThread().getName() + " end B" + System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
return 100;
}
}
每個任務成功返回100,並且任務之間運行的方式是異步的,但join()方法卻是同步的。
RecursiveTask實現字符串累加
public class MyTestE {
public static void main(String[] args) throws InterruptedException {
ForkJoinPool pool = new ForkJoinPool();
MyRecursiveTaskForString task = new MyRecursiveTaskForString(1, 20);
ForkJoinTask<String> task1 = pool.submit(task);
System.out.println(task1.join());
Thread.sleep(5000);
}
}
class MyRecursiveTaskForString extends RecursiveTask<String> {
private int beginValue;
private int endValue;
public MyRecursiveTaskForString(int beginValue, int endValue) {
this.beginValue = beginValue;
this.endValue = endValue;
}
@Override
protected String compute() {
System.out.println(Thread.currentThread().getName() + " ------------");
if (endValue - beginValue > 2) {
int middleValue = (endValue + beginValue) / 2;
MyRecursiveTaskForString leftTask = new MyRecursiveTaskForString(beginValue, middleValue);
MyRecursiveTaskForString rightTask = new MyRecursiveTaskForString(middleValue + 1, endValue);
invokeAll(leftTask, rightTask);
return leftTask.join() + rightTask.join();
} else {
String returnString = "";
for (int i = beginValue; i <= endValue;i++) {
returnString = returnString + (i);
}
System.out.println("else返回:" + returnString + " " + beginValue + " " + endValue);
return returnString;
}
}
}
使用Fork-Join實現求和:示例1
public class MyTestF {
public static void main(String[] args) {
try {
MyRecursiveTask task = new MyRecursiveTask(1, 10);
ForkJoinPool pool = new ForkJoinPool();
pool.submit(task);
System.out.println("結果值爲:" + task.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
class MyRecursiveTask extends RecursiveTask<Integer> {
private int beginPosition;
private int endPosition;
public MyRecursiveTask(int beginPosition, int endPosition) {
this.beginPosition = beginPosition;
this.endPosition = endPosition;
System.out.println("# " + (beginPosition + " " + endPosition));
}
@Override
protected Integer compute() {
System.out.println(Thread.currentThread().getName() + "--------");
Integer sumValue = 0;
System.out.println("compute=" + beginPosition + " " + endPosition);
if ((endPosition - beginPosition) != 0) {
System.out.println("!=0");
int middleNum = (endPosition + beginPosition) / 2;
System.out.println("left 傳入的值:" + (beginPosition + " " + middleNum));
MyRecursiveTask leftTask = new MyRecursiveTask(beginPosition, middleNum);
System.out.println("right 傳入的值:" + (middleNum + 1) + " " + endPosition);
MyRecursiveTask rightTask = new MyRecursiveTask(middleNum + 1, endPosition);
invokeAll(leftTask, rightTask);
Integer left = leftTask.join();
Integer right = rightTask.join();
return left + right;
} else {
return endPosition;
}
}
}
使用Fork-Join實現求和:示例2
public class MyTestG {
public static void main(String[] args) {
MyRecursiveTaskSum task = new MyRecursiveTaskSum(1, 10);
ForkJoinPool pool = new ForkJoinPool();
ForkJoinTask<Integer> submit = pool.submit(task);
System.out.println("結果值爲:" + submit.join());
}
}
class MyRecursiveTaskSum extends RecursiveTask<Integer> {
private int beginPostion;
private int endPosition;
public MyRecursiveTaskSum(int beginPostion, int endPosition) {
this.beginPostion = beginPostion;
this.endPosition = endPosition;
System.out.println("# " + (beginPostion + " " + endPosition));
}
@Override
protected Integer compute() {
Integer sumValue = 0;
System.out.println("compute=" + beginPostion + " " + endPosition);
if ((endPosition - beginPostion) > 2) {
System.out.println("!=0");
int middleNum = (endPosition + beginPostion) / 2;
System.out.println("left 傳入的值:" + (beginPostion + " " + middleNum));
MyRecursiveTaskSum leftTask = new MyRecursiveTaskSum(beginPostion, middleNum);
System.out.println("right 傳入的值:" + (middleNum + 1) + " " + endPosition);
MyRecursiveTaskSum rightTask = new MyRecursiveTaskSum(middleNum + 1, endPosition);
invokeAll(leftTask, rightTask);
Integer leftValue = leftTask.join();
Integer rightValue = rightTask.join();
System.out.println("++++++++++++++++++" + (leftValue + rightValue));
return leftValue + rightValue;
} else {
int count = 0;
for (int i = beginPostion; i <= endPosition; i++) {
count += i;
}
return count;
}
}
}
核心條件爲if((endPosition-beginPosition)>2)
,也就是可以使用else{}中的for循環以數字範圍的方式進行累加求和。