我刚刚实现了合并排序的线程版本。 ThreadedMerge.java :http://pastebin.com/5ZEvU6BV
由于merge sort是一种分而治之的算法,我为数组的每一半创建一个线程。但是Java-VM中可用线程的数量是有限的,所以我在创建线程之前检查它:
if(num <= nrOfProcessors){
num += 2;
//create more threads
}else{
//continue without threading
}
然而,线程排序大约需要~ 6000 ms
,而非线程版本只需~ 2500 ms
就快得多。
非线程:http://pastebin.com/7FdhZ4Fw
为什么线程版本较慢,如何解决该问题?
更新:我现在使用atomic integer
进行线程计数,并为Runtime.getRuntime().availableProcessors()
声明了一个静态字段。现在排序大约需要~ 1400 ms
。
但是,在mergeSort方法中只创建一个线程并让当前线程执行其余操作并没有显着的性能提升。的为什么吗
此外,当我在一个线程上调用join之后,然后使用
减少使用线程的数量num.set(num.intValue() - 1);
排序大约需要~ 200 ms
。这是我的算法的更新http://pastebin.com/NTZq5zQp为什么这行代码会使它更糟?
答案 0 :(得分:4)
首先,您对num的访问不是线程安全的(检查http://download.oracle.com/javase/6/docs/api/java/util/concurrent/atomic/AtomicInteger.html)
您为核心创建了相同数量的进程,但是您通过联接调用阻止其中一半进程
num += 1;
ThreadedMerge tm1 = new ThreadedMerge(array, startIndex, startIndex + halfLength);
tm1.start();
sortedRightPart = mergeSort(array, startIndex + halfLength, endIndex);
try{
tm1.join();
num-=1
sortedLeftPart = tm1.list;
}catch(InterruptedException e){
}
这不会阻塞调用线程,但是使用它来对正确的部分进行排序,并让创建的线程执行其他部分,当它返回其占用的空间时,可以被另一个线程使用
答案 1 :(得分:3)
嗯,你不应该为每一步创建一个线程(它们很昂贵且有轻量级替代品。)
理想情况下,如果有4个CPU,则只应创建4个线程。
所以假设你有4个CPU,然后你在第一级创建一个线程(现在你有2个),在第二个级别你也创建一个新线程。这给你4。
您只创建一个而不是两个的原因是您可以使用当前运行的线程,如:
Thread t = new Thread(...);
t.start();
// Do half of the job here
t.join(); // Wait for the other half to complete.
如果你有5个CPU(不是2的幂),那么只需创建8个线程。
在实践中执行此操作的一种简单方法是创建在达到适当级别时已经创建的非线程版本。通过这种方式,当if-sentence等时,你可以避免混淆合并方法。
答案 2 :(得分:1)
Runtime.availableProcessors()
的来电似乎占用了相当多的额外时间。你只需要调用一次,所以只需将它移到方法之外并将其定义为静态,例如:
static int nrOfProcessors = Runtime.getRuntime().availableProcessors();