一、分而治之原理(fork/join )
在計算機十大經典算法中,快速排序
、歸併排序
和二分查找
用的是分而治之原理。
1、定義
在Java的Fork/Join框架中,使用兩個類完成上述操作
- 1、ForkJoinTask:我們要使用Fork/Join框架,首先需要創建一個ForkJoin任務。該類提供了在任務中執行fork和join的機制。通常情況下我們不需要直接集成ForkJoinTask類,只需要繼承它的子類,Fork/Join框架提供了兩個子類:
RecursiveTask
的同步用法
同時演示有返回結果
,統計整形數組中所有元素的和。RecursiveAction
的異步用法
同時演示不要求返回值
,遍歷指定目標(含子目錄)尋找指定類型文件。
- 2、ForkJoinPool:ForkJoinTask需要通過
ForkJoinPool
來執行。- 任務分割出的子任務會添加到當前工作線程所維護的雙端隊列中,進入隊列的頭部。當一個工作線程的隊列裏暫時沒有任務時,它會隨機從其他工作線程的隊列的尾部獲取一個任務(
工作竊取算法
)。
2、歸併排序-同步用法
2.1、數組集合
public class MakeArray {
public static final int MAX_COUNT = 40000;
public static int[] getArrays(){
int[] nums = new int[MAX_COUNT];
Random random = new Random();
for (int i = 0; i < MAX_COUNT; i++) {
nums[i] = random.nextInt(MAX_COUNT);
}
return nums;
}
}
2.2、求數組中的和
public class SunArray {
public static class SumTask extends RecursiveTask<Integer>{
private static final int THRESHOLD = MakeArray.MAX_COUNT/10;
private int[] nums;
private int fromIndex;
private int toIndex;
public SumTask(int[] nums, int fromIndex, int toIndex) {
this.nums = nums;
this.fromIndex = fromIndex;
this.toIndex = toIndex;
}
@Override
protected Integer compute() { //運用遞歸算法
if (toIndex - fromIndex < THRESHOLD){
System.out.println("form index = " + fromIndex + "toIndex = " + toIndex);
int count = 0;
for (int i = fromIndex; i < toIndex; i++) {
count += nums[i];
}
return count;
} else {
int mid = (toIndex + fromIndex) / 2;
SumTask left = new SumTask(nums, fromIndex, mid);
SumTask right = new SumTask(nums, mid, toIndex);
invokeAll(left, right);
return left.join() + right.join();
}
}
}
public static void main(String[] argc){
int[] arrays = MakeArray.getArrays();
ForkJoinPool forkJoinPool = new ForkJoinPool();
SumTask sumTask = new SumTask(arrays, 0, arrays.length);
long start = System.currentTimeMillis();
forkJoinPool.invoke(sumTask);
System.out.println("The count is" + sumTask.join() +
"spend time" + (System.currentTimeMillis() - start) + "ms");
}
}
3、異步用法
/**
*類說明:遍歷指定目錄(含子目錄)找尋指定類型文件
*/
public class FindDirsFiles extends RecursiveAction {
private File path;
public FindDirsFiles(File path) {
this.path = path;
}
@Override
protected void compute() {
List<FindDirsFiles> subTasks = new ArrayList<>();
File[] files = path.listFiles();
if (files!=null){
for (File file : files) {
if (file.isDirectory()) {
// 對每個子目錄都新建一個子任務。
subTasks.add(new FindDirsFiles(file));
} else {
// 遇到文件,檢查。
if (file.getAbsolutePath().endsWith("txt")){
System.out.println("文件:" + file.getAbsolutePath());
}
}
}
if (!subTasks.isEmpty()) {
// 在當前的 ForkJoinPool 上調度所有的子任務。
for (FindDirsFiles subTask : invokeAll(subTasks)) {
subTask.join();
}
}
}
}
public static void main(String [] args){
try {
// 用一個 ForkJoinPool 實例調度總任務
ForkJoinPool pool = new ForkJoinPool();
FindDirsFiles task = new FindDirsFiles(new File("F:/"));
/*異步提交*/
pool.execute(task);
/*主線程做自己的業務工作*/
System.out.println("Task is Running......");
Thread.sleep(1);
int otherWork = 0;
for(int i=0;i<100;i++){
otherWork = otherWork+i;
}
System.out.println("Main Thread done sth......,otherWork=" +otherWork);
task.join();//阻塞方法
System.out.println("Task end");
} catch (Exception e) {
e.printStackTrace();
}
}
}
二、CountDownLatch 計數器
- countDownLatch這個類使一個線程等待其他線程各自執行完畢後再執行。
- 是通過一個計數器來實現的,計數器的初始值是線程的數量。每當一個線程執行完畢後,計數器的值就-1,當計數器的值爲0時,閉鎖上等待的線程就可以恢復工作了。
- 使用AQS的共享方式,內部實現了
AbstractQueuedSynchronizer
的內部類。
private static final class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 4982264981922014374L;
Sync(int count) {
setState(count);
}
int getCount() {
return getState();
}
protected int tryAcquireShared(int acquires) {
return (getState() == 0) ? 1 : -1;
}
protected boolean tryReleaseShared(int releases) {
// Decrement count; signal when transition to zero
for (;;) {
int c = getState();
if (c == 0)
return false;
int nextc = c - 1;
if (compareAndSetState(c, nextc))
return nextc == 0;
}
}
}
注意:一個線程可以多次減一;閉鎖線程可以有多個且閉鎖線程執行任務時其他線程可能還在執行
示例演示
/**
*類說明:演示CountDownLatch用法,
* 共5個初始化子線程,6個閉鎖釦除點,扣除完畢後,主線程和業務線程才能繼續執行
*/
public class UseCountDownLatch {
static CountDownLatch latch = new CountDownLatch(6);
/*初始化線程*/
private static class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("Thread_" + Thread.currentThread().getId()
+ " ready init work......");
latch.countDown();
for (int i = 0; i < 2; i++) {
System.out.println("Thread_" + Thread.currentThread().getId()
+ " ........continue do its work");
}
}
}
/*業務線程等待latch的計數器爲0完成*/
private static class MyThread implements Runnable {
@Override
public void run() {
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < 3; i++) {
System.out.println("BusiThread_" + Thread.currentThread().getId()
+ " do business-----");
}
}
}
public static void main(String[] args) throws InterruptedException {
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1);
System.out.println("Thread_" + Thread.currentThread().getId()
+ " ready init work step 1st......");
latch.countDown();
System.out.println("begin step 2nd.......");
Thread.sleep(1);
System.out.println("Thread_" + Thread.currentThread().getId()
+ " ready init work step 2nd......");
latch.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
new Thread(new MyThread()).start();
for (int i = 0; i <= 3; i++) {
Thread thread = new Thread(new MyRunnable());
thread.start();
}
latch.await();
System.out.println("Main do ites work........");
}
}