技术开发 频道

通过线程门实现Java并发编程

  搜索执行器

  划分搜索空间和分配连续块给不同线程的任务由类com.javaworld.primefinder.GatedPrimeNumberSearcher来完成;这是契约com.javaworld.primefinder.PrimeNumberSearcher的一个实现。

  在实例化后,这种类型的对象判断系统处理器的数量,并根据它来将搜索空间划分为近似大小的块,然后将它们分配给不同的任务。块信息被封装在com.javaworld.primefinder.PartitionInfo类中,如列表8所示。

  列表8.搜索空间分区信息的封装

1 class PartitionInfo
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.搜索空间块划分的方法

1private PartitionInfo getPartitionInfo(long lowerBound,
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()的实现

1  public PrimeNumberSource findPrimeNumbers(BigInteger aLowerBound,
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.素数消费者

1   @Override
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)》一书和其它相关书籍。

0
相关文章