Java多线程——Fork/Join框架

Fork/Join框架是Java中用于并行处理的一种机制,通过将大任务分解为小任务并合并结果来提高CPU利用率。ForkJoinTask是基本的并发单元,包括RecursiveAction和RecursiveTask子类。ForkJoinPool负责调度这些任务,并使用工作窃取策略优化任务分配。当任务足够小,直接执行;否则,继续拆分并异步执行,最后合并结果。该框架在Arrays.parallelSort等并行操作中有广泛应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

使用多线程技术,让不同的任务在不同的线程中运行,可以提高CPU的利用率。然而只是仅仅通过任务来划分,粒度还是有点大。当任务中有些任务运行所占时间远远大于其它任务的时候,使用多线程并发所带来的效果并不明显。在这种情况下把耗时长的大任务划分为耗时短的小任务,可以有效地缓解并发的瓶颈,最终把小任务的结果合并获取完整的任务的结果。Fork/Join就可以很好地实现这种划分任务最后合并的思路(你可以把它当做并发版的分治)。


一、ForkJoinTask & ForkJoinPool

1. ForkJoinTask<V>

扩展自Future,它代表的是比任务更加轻量级,细粒度的并发单元。它本身是一个抽象类,这里提两个重要的抽象子类:RecursiveAction和RecursiveTask<V>,通过重写它们的compute方法可以定义任务,要注意的是前者的compute是不带返回值的,而后者的是有返回值的。

a. fork()方法允许ForkJoinTask任务异步执行,也允许一个新的ForkJoinTask从存在的ForkJoinTask中被启动。变种: invoke(), invokeAll()

b. 反过来, join()方法允许一个ForkJoinTask等待另一个ForkJoinTask执行完成。变种: get()


2. ForkJoinPool

继承自AbstractExecutorService,用于调度ForkJoinTask。


3. 调度机制

一个主的ForkJoinTask被提交到ForkJoinPool之后开始执行。然后ForkJoinTask开始(通过fork()等方法)启动它的子任务,并(通过get()等方法)等待它们完成。



二、使用Fork/Join框架

下面是最经典的Fork/Join框架的使用场景:

if task is small enough

execute the task

else

divide it into several smaller tasks

invoke all tasks

group results


例子:

import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveTask;
import java.util.concurrent.TimeUnit;

public class FJCount extends RecursiveTask {
	private String document[];
	private int start, end;
	private String word;

	FJCount (String _d[], int _s, int _e, String _w) {
		document = _d;
		start = _s;
		end = _e;
		word = _w;
	}
	@Override
	protected Object compute() {
		int result = 0;
		if (end - start < 10) {
			result = count(document, start, end, word);
		} else {
			int mid = (start + end) / 2;
			FJCount task1 = new FJCount(document, start, mid, word);
			FJCount task2 = new FJCount(document, mid+1, end, word);
			invokeAll(task1, task2);
		
			try {
				result = (int)task1.get() + (int)task2.get();
			} catch (InterruptedException | ExecutionException e) {
				e.printStackTrace();
			}
		}
		
		return result;
	}
	
	int count(String []document, int start, int end, String word) {
		int counter = 0;
		
		for (int i = start; i <= end; ++i) {
			if (document[i] == word) {
				++counter;
			}
		}
		
		return counter;
	}

	public static void main(String ...arg) {
		String document[] = new String[100];
		
		for (int i = 0; i < 100; ++i) {
			if (i%10 == 0)
				document[i] = "Test";
			else
				document[i] = " ";
		}
		
		FJCount test = new FJCount(document, 0, 99,"Test");
		ForkJoinPool pool = new ForkJoinPool();
		pool.execute(test);
		
		do {
			System.out.println("****************************");
			System.out.printf("Main: Paralleism: %d\n", pool.getParallelism());
			System.out.printf("Main: Active Threads: %d\n", pool.getActiveThreadCount());
			System.out.printf("Main: Task Count: %d\n", pool.getQueuedTaskCount());
			System.out.printf("Main: Steal Count: %d\n", pool.getStealCount());
			
			try {
				TimeUnit.SECONDS.sleep(1);
			}catch (InterruptedException e){
				e.printStackTrace();
			}
		} while (!test.isDone());
		
		pool.shutdown();
		
		try {
			pool.awaitTermination(1,  TimeUnit.DAYS);
		}catch (InterruptedException e) {
			e.printStackTrace();
		}
		
		try {
			System.out.printf("Main: The word appears %d times in the document", test.get());
		} catch (InterruptedException | ExecutionException e) {
			e.printStackTrace();
		}		
	}
}


三、工作窃取

ForkJoinPool维护每个线程的任务列表,当某个任务完成时,它可以把挂在满负荷线程上的任务重新安排到空闲线程上去。就好比两个人搬砖,先搬完的回去帮另外那个人搬剩下的砖。这就是所谓的工作窃取,它解决了不同大小的任务之间的调度问题。工作窃取特性是ForkJoinPool和一些其它的ExecutorService的区别之一。


四、总结

Fork/Join通过把任务分割成更细粒度的单元,可以有效地减少瓶颈,提高CPU的使用率。当问题可以被分成很多可以并行处理的子问题的时候可以考虑使用Fork/Join。比如说Arrays的parallelSort方法就是通过Fork/Join来实现的。


Reference:

http://docs.oracle.com/javase/tutorial/essential/concurrency/forkjoin.html

http://www.oracle.com/technetwork/articles/java/fork-join-422606.html

http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ForkJoinTask.html

http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ForkJoinPool.html

《Java 7 Concurrency Cookbook》

《The Well-Grounded Java Developer》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值