08-案例实现
案例实现
根据如下步骤来实现本案例:
1.创建一个名为 DocumentMock
的类。该类通过生成一个字符串型二维数组来模拟一份文档:
public class DocumentMock {
2.创建一个由多个单词组成的字符串型数组。该数组用于生成字符串型二维数组:
private String words[]={"the","hello","goodbye","packt",
"java","thread","pool","random",
"class","main"};
3.实现 generateDocument()
方法。该方法接收行数、每行的单词数和要搜索的单词作为参数,并返回一个字符串型二维数组:
public String[][] generateDocument(int numLines, int numWords,
String word){
4.创建必要的对象来生成文档—二维数组 String
和用来生成随机数字的 Random
对象:
int counter=0;
String document[][]=new String[numLines][numWords];
Random random=new Random();
5.用字符串填充数组。数组的每个位置都存储字符串,数组中字符串对应的位置是随机生成的。程序要在生成的数组中查找给定单词出现的次数。开发者可以用该值来检查程序是否正确完成任务:
for (int i=0; i<numLines; i++){
for (int j=0; j<numWords; j++) {
int index=random.nextInt(words.length);
document[i][j]=words[index];
if (document[i][j].equals(word)){
counter++;
}
}
}
6.打印单词出现的次数,并返回生成的二维数组:
System.out.println("DocumentMock: The word appears "+ counter+"
times in the document");
return document;
7.创建一个名为 DocumentTask
的类,并继承泛型类型为 Integer
的 RecursiveTask
类。该类实现了指定单词在行集合中出现次数的任务:
public class DocumentTask extends RecursiveTask<Integer> {
8.声明一个名为 document
的私有 String
型数组、名为 start
和 end
的两个私有 int
型属性,并声明一个名为 word
的私有 String
型属性:
private String document[][];
private int start, end;
private String word;
9.实现该类的构造方法并初始化全部属性:
public DocumentTask (String document[][], int start, int end,
String word){
this.document=document;
this.start=start;
this.end=end;
this.word=word;
}
10.实现 compute()
方法。如果 end
和 start
属性的差小于 10
,则该任务直接计算通过调用 processLines()
方法获得的行中,给定单词出现的次数:
@Override
protected Integer compute() {
Integer result=null;
if (end-start<10){
result=processLines(document, start, end, word);
11.否则,分割行为两组,并创建两个新的 DocumentTask
对象来处理这两组行,并在池中调用 invokeAll()
方法来执行它们:
} else {
int mid=(start+end)/2;
DocumentTask task1=new DocumentTask(document,start,mid,word);
DocumentTask task2=new DocumentTask(document,mid,end,word);
invokeAll(task1,task2);
12.调用 groupResults()
方法,将两个任务返回的值相加,并返回任务执行的最终结果:
try {
result=groupResults(task1.get(),task2.get());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
return result;
13.实现 processLines()
方法。该方法接收一个字符串二维数组,以 start
、 end
属性和用于查找单词的 word
属性作为参数:
private Integer processLines(String[][] document, int start,
int end,String word) {
14.为任务中需要处理的每一行创建一个 LineTask
对象,并用一个任务列表来存储:
List<LineTask> tasks=new ArrayList<LineTask>();
for (int i=start; i<end; i++){
LineTask task=new LineTask(document[i], 0,
document[i].length, word);
tasks.add(task);
}
15.用 invokeAll()
方法执行全部任务:
invokeAll(tasks);
16.汇总所有任务的返回值,并返回最终结果:
int result=0;
for (int i=0; i<tasks.size(); i++) {
LineTask task=tasks.get(i);
try {
result=result+task.get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
return result;
17.实现 groupResults()
方法。该方法将两个数组相加并返回结果:
private Integer groupResults(Integer number1,Integer number2) {
Integer result;
result=number1+number2;
return result;
}
18.创建一个名为 LineTask
的类,泛型类型为 Integer
并继承 RecursiveTask
类。该类将计算一行中给定单词出现的次数:
public class LineTask extends RecursiveTask<Integer>{
19.声明一个名为 line
的私有 String
型数组,名为 start
和 end
的两个私有 int
型属性。最后,声明一个名为 word
的私有 String
型属性:
private String line[];
private int start, end;
private String word;
20.实现该类的构造方法并初始化全部属性:
public LineTask(String line[],int start,int end,String word) {
this.line=line;
this.start=start;
this.end=end;
this.word=word;
}
21.实现该类的 compute()
方法。如果 end
和 start
属性的差小于 100
,则该任务直接调用 count()
方法并在行中 start
和 end
范围内搜索给定单词:
@Override
protected Integer compute() {
Integer result=null;
if (end-start<100) {
result=count(line, start, end, word);
22.否则,将行分割为两组,并创建两个新的 LineTask
对象来处理这两组行,并在池中调用 invokeAll()
方法来执行它们:
} else {
int mid=(start+end)/2;
LineTask task1=new LineTask(line, start, mid, word);
LineTask task2=new LineTask(line, mid, end, word);
invokeAll(task1, task2);
23.调用 groupResults()方
法,将两个任务返回的值相加,并返回任务的最终结果:
try {
result=groupResults(task1.get(),task2.get());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
return result;
24.实现 count()
方法。该方法接收由完整一行生成的字符串二维数组, start
、 end
属性和用于查找单词的 word
属性作为参数:
private Integer count(String[] line, int start, int end,
String word) {
25.在 start
和 end
之间,对比需要搜索的单词和数组对应位置上存储的单词。如果两个单词相同,则增加 counter
变量:
int counter;
counter=0;
for (int i=start; i<end; i++){
if (line[i].equals(word)){
counter++;
}
}
26.让任务休眠10ms,从而减慢程序运行:
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
27.返回 counter
变量的值:
return counter;
28.实现 groupResults()
方法,该方法将两个数字相加并返回结果:
private Integer groupResults(Integer number1,Integer number2) {
Integer result;
result=number1+number2;
return result;
}
29.至此,就可以实现应用程序的入口,创建包含 main()
方法的 Main
类:
public class Main{
public static void main(String[] args) {
30.用 DocumentMock
类来创建一个包含100行,每行有1000个单词的文档:
DocumentMock mock=new DocumentMock();
String[][] document=mock.generateDocument(100, 1000, "the");
31.创建一个新的 DocumentTask
对象来更新整个文档。 start
参数值为 0
, end
参数值为 100
:
DocumentTask task=new DocumentTask(document, 0, 100, "the");
32.调用 commonPool()
方法获取默认的 ForkJoinPool
执行器,并调用 execute()
方法执行该任务:
ForkJoinPool commonPool=ForkJoinPool.commonPool();
commonPool.execute(task);
33.实现一个代码块,用于展示池中进程的信息。任务结束之前,每秒在控制台打印输出池中参数值一次:
do {
System.out.printf("*************************
*****************\n");
System.out.printf("Main: Active Threads: %d\n",
commonPool.getActiveThreadCount());
System.out.printf("Main: Task Count: %d\n",
commonPool.getQueuedTaskCount());
System.out.printf("Main: Steal Count: %d\n",
commonPool.getStealCount());
System.out.printf("***********************************
*******\n");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
} while (!task.isDone());
34.调用 shutdown()
方法关闭池:
pool.shutdown();
35.调用 awaitTermination()
方法等待程序执行完成:
try {
pool.awaitTermination(1, TimeUnit.DAYS);
} catch (InterruptedException e) {
e.printStackTrace();
}
36.打印文档中单词出现的次数,检查该数字是否和 DocumentMock
类中获取的数字相同:
try {
System.out.printf("Main: The word appears %d in the
document",task.get());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}