搜索执行器
划分搜索空间和分配连续块给不同线程的任务由类com.javaworld.primefinder.GatedPrimeNumberSearcher来完成;这是契约com.javaworld.primefinder.PrimeNumberSearcher的一个实现。
在实例化后,这种类型的对象判断系统处理器的数量,并根据它来将搜索空间划分为近似大小的块,然后将它们分配给不同的任务。块信息被封装在com.javaworld.primefinder.PartitionInfo类中,如列表8所示。
列表8.搜索空间分区信息的封装
2
3 {
4
5 PartitionInfo(int numberOfBuckets, long bucketSize)
6
7 public int getNumberOfBuckets().....
8
9 public long getBucketSize() ........
10
11 }
这个类的实例由列表9中的方法来创建,该操作并定义在com.javaworld.primefinder.GatedPrimeNumberSearcher任务/搜索线程实现中。
列表9.搜索空间块划分的方法
2
3 long upperBound)
4
5 {
6
7 PartitionInfo result;
8
9 int proposedBucketCount = numberOfProcessors;
10
11 long bucketSize = (upperBound-lowerBound) /
12
13 proposedBucketCount;
14
15 result = new PartitionInfo(proposedBucketCount,
16
17 bucketSize);
18
19 return result;
20
21 }
在列表10中你可以发现,PartitionInfo实例被用来创建一个或多个搜索线程,或者更准确的说,创建一个或多个可运行的任务/目标,由com.javaworld.primefinder类来实现。除了搜索范围信息外,每一个线程还具有一个指向结果句柄的指针,以允许它将可用的搜索结果写到该句柄的内部结果缓冲器中。
列表10.契约findPrimeNumbers()的实现
2
3 BigInteger aUpperBound)
4
5 {
6
7 if (aUpperBound.longValue()<=aLowerBound.longValue())
8
9 throw new IllegalArgumentException("Upperbound must be
10
11 greater than lowerbound");
12
13 long lowerBound = aLowerBound.longValue(),
14
15 upperBound = aUpperBound.longValue();
16
17 final ConcurrentPrimeNumberSource result;
18
19 PartitionInfo partitionInfo =getPartitionInfo(lowerBound,
20
21 upperBound);
22
23 result = new ConcurrentPrimeNumberSource(
24
25 partitionInfo.getNumberOfBuckets());
26
27 Thread searchThread;
28
29 long shiftingLowerBound = lowerBound,
30
31 shiftingUpperBound = partitionInfo.getBucketSize();
32
33 while (shiftingUpperBound <= upperBound)
34
35 {
36
37 searchThread =
38
39 new Thread(new PrimeSearchThread(
40
41 BigInteger.valueOf(shiftingLowerBound),
42
43 BigInteger.valueOf(shiftingUpperBound),
44
45 result));
46
47 searchThread.start();
48
49 shiftingLowerBound = shiftingUpperBound;
50
51 shiftingUpperBound+= partitionInfo.getBucketSize();
52
53 }
54
55 return result;
56
57 }
在调试测试程序之前,还有一个应用程序类需要提及:消费者/读取器(consumer/reader)线程实现类com.javaworld.primefinder.PrimeNumberReader,其核心方法如列表11所示。
列表11.素数消费者
2
3 public void run()
4
5 {
6
7 BigInteger nextVal=null;
8
9 do
10
11 {
12
13 nextVal = source.nextPrime();
14
15 if (nextVal!=null)
16
17 System.out.println(nextVal + ", ");
18
19 else break;
20
21 }while (true);
22
23 }
在JUnit 4测试工具中,这个类的几个实例用来模拟多个客户端线程在线程门中排队的效果。
测试工具包
在本篇文章源文件的test文件夹下有一个名为com.javaworld.primefinder.PrimeFinderTest的JUnit 4测试包。这个测试类会创建一个GatedPimeNumberSearcher实例,来搜索从1到适当上限的素数;它还创建几个阅读器线程,来打印出系统输出的结果数据。你需要根据你的计算机的处理能力来调整搜索空间的上限值,然后运行测试来观察线程门的执行。
结论
本篇文章介绍了线程门的概念,并演示了如何实现和使用一个基本的门类。另外还提供了源示例和源代码来让读者进一步理解这一概念。如果想了解本文中所提到的多线程问题,你可以参阅一下并发专家 Brian Goetz所编写的《Java并发实践(http://www.javaworld.com/javaworld/jw-09-2006/jw-0904-threads.html)》一书和其它相关书籍。