我有一个名称的txt文件,我需要能够按字母顺序排序。然后我的程序获取数组,分成在终端中作为参数传递的线程数量,并为每个线程提供一个数组进行排序,然后将所有线程存储在一个数组中。现在,我需要一些帮助的是: 我现在想要一旦它们完成就接受线程(即如果两个在其他之前完成,它们开始合并然后等待更多)。把它想象成编织。我知道如何为合并编写排序代码,但我希望你能帮助我的是:我如何管理线程?我知道wait()和notify()会做什么,但我似乎无法将我的ead包装成我需要做什么才能将它们合并到一个数组中。我应该:
我希望这个问题足够清楚,而且质量应该足够好。
答案 0 :(得分:2)
我认为您应该使用Merge Sort算法,并将其实现基于ForkJoinPool(当然,如果您正在使用Java 7)。
此算法非常适合,因为作业可以拆分为独立任务,可以由不同的线程处理。现在,ForkJoinPool
为您提供了易于使用的池,您可以在其中提交排序任务。
实施应该这样做:
.sort()
方法进行排序,否则它分成两半,这两半是提交到池进行排序; 以下是算法的示例实现。请注意,这个距离最佳,因为它消耗了大量的额外内存。我实现了它,以实现方法。使用-Xmx1024m
运行它。
public class ForkJoinSort {
private static final int LIST_SIZE = 10000;
private static final int SORT_THRESHOLD = 10; //the minimal length of the list to use standard java sort rather than mergesort
private static ForkJoinPool forkJoinPool = new ForkJoinPool();
public static class MergeSortTask extends RecursiveTask<List<Integer>> {
private final List<Integer> victim;
public MergeSortTask(List<Integer> victim) {
this.victim = victim;
}
@Override
protected List<Integer> compute() {
if (victim.size() < SORT_THRESHOLD) {
Collections.sort(victim);
return victim;
}
//sorting left and right parts of the list separately in separate threads
MergeSortTask leftTask = new MergeSortTask(victim.subList(0, victim.size() / 2));
MergeSortTask rightTask = new MergeSortTask(victim.subList(victim.size() / 2, victim.size()));
forkJoinPool.submit(leftTask);
forkJoinPool.submit(rightTask);
//do merge
return merge(leftTask.join(), rightTask.join());
}
public List<Integer> merge(List<Integer> left, List<Integer> right) {
List<Integer> result = new ArrayList<Integer>(left.size() + right.size());
Iterator<Integer> leftIterator = left.iterator();
Iterator<Integer> rightIterator = right.iterator();
Integer fromLeft = null;
Integer fromRight = null;
while (leftIterator.hasNext() || rightIterator.hasNext()) {
//if current value taken from the iterator is null - take new one if possible, otherwise do nothing
fromLeft = fromLeft == null ? leftIterator.hasNext() ? leftIterator.next() : null : fromLeft;
fromRight = fromRight == null ? rightIterator.hasNext() ? rightIterator.next() : null : fromRight;
if (fromLeft != null && (fromRight == null || fromLeft <= fromRight)) {
result.add(fromLeft);
fromLeft = null; //this is done to indicate that value from left iterator already passed to result list
} else if (fromRight != null && (fromLeft == null || fromRight <= fromLeft)) {
result.add(fromRight);
fromRight = null;
}
}
return result;
}
}
public static void main(String[] args) throws Exception {
SecureRandom random = new SecureRandom();
//generate array of random numbers
List<Integer> victim = new ArrayList<Integer>(LIST_SIZE);
for (int i = 0; i < LIST_SIZE; ++i) {
victim.add(random.nextInt());
}
//do some benchmarking as long as we're here
long timeMark = System.currentTimeMillis();
MergeSortTask task = new MergeSortTask(victim);
forkJoinPool.submit(task);
List<Integer> probablySorted = task.get();
timeMark = System.currentTimeMillis() - timeMark;
//asserting that array is sorted
for (int i = 0; i < probablySorted.size() - 1; ++i) {
if (probablySorted.get(i) > probablySorted.get(i + 1)) {
throw new IllegalStateException("Sorting failed :(");
}
}
System.out.println("Sorting " + LIST_SIZE + " random numbers using merge sort algorithm in " + Runtime.getRuntime().availableProcessors() + " threads took " + timeMark + " ms.");
}
}
我试图使代码易于阅读。如果我在某个地方失败了,请不要犹豫。
答案 1 :(得分:1)
正如@Alexey正确地指出进行并行排序的最简单方法肯定是使用fork / join框架并合并排序。这很容易做,看起来像(伪代码):
def mergesort(a, i0, i1):
if i0 == i1:
return
im = i0 + (i1 - i0) / 2
fork mergesort(a, i0, im)
fork mergesort(a, im, i1)
join
merge(a, i0, im, i1) # serial merge
如果我们分析这个,我们看到我们有(很容易通过主定理展示):
Work: T_1(n) = 2T_1(n / 2) + O(n) = O(n lg n)
Span: T_inf(n) = 1 T_inf(n / 2) + O(n) = O(n)
其中work表示完成的工作总量,span表示如果我们有无限多个线程可用的时间(基本上是树的深度)需要多长时间。
算法所具有的并行性基本上是Work / Span,在这种情况下给我们O(lg n)
- 这实际上是无关紧要的,尽管如果我们使用一个好的串行排序算法来获得足够小的叶子大小,这仍然可以很好地工作。
我们可以通过并行化合并做得更好。这个可以在没有辅助阵列的情况下完成,但我会将其作为练习给读者留下(意思是:不容易,我不得不查看如何实际操作)。
并行合并:假设我们在[i0,i1]和[j0,j1]中有一个带有两个排序数组的辅助数组aux,我们希望将合并后的子数组放入k0,k1之间的数组a中。我们再次递归地执行此操作:
困惑?好吧,下面的例子(我在CS而不是艺术......)应该有所帮助:
在代码中,这看起来像
def merge(a, aux, i0, i1, j0, j1, k0, k1):
if i0 == i1:
copy aux[j0, j1] to a[k0, k1]
return
if j0 == j1:
copy aux[i0, i1] to a[k0, k1]
return
im = im = i0 + (i1 - i0) / 2
jm = find(aux, j0, j1, aux[im])
km = k0 + (im - i0) + 1 + (jm - j0 )
a[km] = aux[im]
fork merge(a, aux, i0, im, j0, jm, k0, km)
fork merge(a, aux, im + 1, i1, jm, j1, km + 1, k1)
join
重要的是要注意,find
必须使用O(lg n)中的简单串行二进制搜索来完成,因为我们知道右侧已经排序。
使用这样的并行合并为我们提供了相同的工作,但是将跨度减小到O(lg ^ 3 n),这转换为O(n / lg ^ 2 n)的并行性 - 这是一个很大的改进。
Nota bene:对于实际中的任何并行算法,如果问题规模太小(快速排序或其他),您将需要使用简单的串行版本 - 必须通过单独评估每个体系结构的叶片大小最佳实验
答案 2 :(得分:1)
我是您所关注的大学课程的助教(以及有关作业的考官)。您已经给出了问题的答案很棒,并且可能描述了与完全顺序排序+合并相比,解决此问题以获得最佳性能和加速的最佳方法。但是,你应该记住,这是面向对象编程的初学者课程,也是你第一次接触并行和多线程的任务。
由于距离截止日期还有14个小时,我不建议您采用先进的方法解决问题,例如扩展诸如ForkJoinPool等库类,并行化双枢轴快速排序等等。这个问题的最简单的解决方案,也是我们给你的任务时我们想到的解决方案,可以按照以下步骤实现:
<强> 算法: 强>
n =线程数
&#34;最初排序的合并排序&#34;
祝你好运!