您的位置:首页 > 数据库 > Oracle

Oracle官方教程之Fork/Join

2016-09-21 13:04 288 查看
ork/join框架是
ExecutorService
接口的一种具体实现,目的是为了帮助你更好地利用多处理器带来的好处。它是为那些能够被递归地拆解成子任务的工作类型量身设计的。其目的在于能够使用所有可用的运算能力来提升你的应用的性能。

类似于
ExecutorService
接口的其他实现,fork/join框架会将任务分发给线程池中的工作线程。fork/join框架的独特之处在与它使用工作窃取(work-stealing)算法。完成自己的工作而处于空闲的工作线程能够从其他仍然处于忙碌(busy)状态的工作线程处窃取等待执行的任务。

fork/join框架的核心是
ForkJoinPool
类,它是对
AbstractExecutorService
类的扩展。
ForkJoinPool
实现了工作偷取算法,并可以执行
ForkJoinTask
任务。

基本使用方法

使用fork/join框架的第一步是编写执行一部分工作的代码。你的代码结构看起来应该与下面所示的伪代码类似:

1
if
(当前这个任务工作量足够小)
2
直接完成这个任务
3
else
4
将这个任务或这部分工作分解成两个部分
5
分别触发(invoke)这两个子任务的执行,并等待结果
你需要将这段代码包裹在一个
ForkJoinTask
的子类中。不过,通常情况下会使用一种更为具体的的类型,或者是
RecursiveTask
(会返回一个结果),或者是
RecursiveAction


当你的
ForkJoinTask
子类准备好了,创建一个代表所有需要完成工作的对象,然后将其作为参数传递给一个
ForkJoinPool
实例的
invoke()
方法即可。

要清晰,先模糊

想要了解fork/join框架的基本工作原理,接下来的这个例子会有所帮助。假设你想要模糊一张图片。原始的source图片由一个整数的数组表示,每个整数表示一个像素点的颜色数值。与source图片相同,模糊之后的destination图片也由一个整数数组表示。

对图片的模糊操作是通过对source数组中的每一个像素点进行处理完成的。处理的过程是这样的:将每个像素点的色值取出,与周围像素的色值(红、黄、蓝三个组成部分)放在一起取平均值,得到的结果被放入destination数组。因为一张图片会由一个很大的数组来表示,这个流程会花费一段较长的时间。如果使用fork/join框架来实现这个模糊算法,你就能够借助多处理器系统的并行处理能力。下面是上述算法结合fork/join框架的一种简单实现:

01
public
class
ForkBlur
extends
RecursiveAction
{
02
private
int
[]
mSource;
03
private
int
mStart;
04
private
int
mLength;
05
private
int
[]
mDestination;
06
07
//
Processing window size; should be odd.
08
private
int
mBlurWidth
=
15
;
09
10
public
ForkBlur(
int
[]
src,
int
start,
int
length,
int
[]
dst) {
11
mSource
= src;
12
mStart
= start;
13
mLength
= length;
14
mDestination
= dst;
15
}
16
17
protected
void
computeDirectly()
{
18
int
sidePixels
= (mBlurWidth -
1
)
/
2
;
19
for
(
int
index
= mStart; index < mStart + mLength; index++) {
20
//
Calculate average.
21
float
rt
=
0
,
gt =
0
,
bt =
0
;
22
for
(
int
mi
= -sidePixels; mi <= sidePixels; mi++) {
23
int
mindex
= Math.min(Math.max(mi + index,
0
),
24
mSource.length
-
1
);
25
int
pixel
= mSource[mindex];
26
rt
+= (
float
)((pixel
&
0x00ff0000
)
>>
16
)
27
  
/
mBlurWidth;
28
gt
+= (
float
)((pixel
&
0x0000ff00
)
>> 
8
)
29
  
/
mBlurWidth;
30
bt
+= (
float
)((pixel
&
0x000000ff
)
>> 
0
)
31
  
/
mBlurWidth;
32
}
33
34
//
Reassemble destination pixel.
35
int
dpixel
= (
0xff000000
)
|
36
   
(((
int
)rt)
<<
16
)
|
37
   
(((
int
)gt)
<<
8
)
|
38
   
(((
int
)bt)
<<
0
);
39
mDestination[index]
= dpixel;
40
}
41
}
接下来你需要实现父类中的
compute()
方法,它会直接执行模糊处理,或者将当前的工作拆分成两个更小的任务。数组的长度可以作为一个简单的阀值来判断任务是应该直接完成还是应该被拆分。

01
protected
static
int
sThreshold
=
100000
;
02
03
protected
void
compute()
{
04
if
(mLength
< sThreshold) {
05
computeDirectly();
06
return
;
07
}
08
09
int
split
= mLength /
2
;
10
11
invokeAll(
new
ForkBlur(mSource,
mStart, split, mDestination),
12
  
new
ForkBlur(mSource,
mStart + split, mLength - split,
13
   
mDestination));
14
}
如果前面这个方法是在一个
RecursiveAction
的子类中,那么设置任务在
ForkJoinPool
中执行就再直观不过了。通常会包含以下一些步骤:

创建一个表示所有需要完成工作的任务。

1
//
source image pixels are in src
2
//
destination image pixels are in dst
3
ForkBlur
fb =
new
ForkBlur(src,
0
,
src.length,dst);
创建将要用来执行任务的
ForkJoinPool


1
ForkJoinPool
pool =
new
ForkJoinPool();
执行任务。

1
pool.invoke(fb);
想要浏览完成的源代码,请查看
ForkBlur
,其中还包含一些创建destination图片文件的额外代码。

标准实现

除了能够使用fork/join框架来实现能够在多处理系统中被并行执行的定制化算法(如前文中的ForkBlur.java例子),在Java SE中一些比较常用的功能点也已经使用fork/join框架来实现了。在Java SE 8中,
java.util.Arrays
类的一系列
parallelSort()
方法就使用了fork/join来实现。这些方法与
sort()
系列方法很类似,但是通过使用fork/join框架,借助了并发来完成相关工作。在多处理器系统中,对大数组的并行排序会比串行排序更快。这些方法究竟是如何运用fork/join框架并不在本教程的讨论范围内。想要了解更多的信息,请参见Java
API文档。

其他采用了fork/join框架的方法还包括
java.util.streams
包中的一些方法,此包是作为Java SE 8发行版中
Project Lambda
的一部分。想要了解更多信息,请参见
Lambda
Expressions
一节。

(全文完)如果您喜欢此文请点赞,分享,评论。

转自Oracle官方教程之Fork/Join
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: