Проблема не в многопоточности: я написал правильно многопоточный QuickSort на Java, и он владеет сортировкой Java по умолчанию. Я сделал это после того, как стал свидетелем процесса обработки гигантского набора данных, и у меня работало только одно ядро 16-ядерного компьютера.
Одна из ваших проблем (огромных) заключается в том, что вы заняты циклом:
// Wait for the two other threads to finish
while(!ma.finished || !mb.finished) ;
Это ОГРОМНО нет-нет: это называется занятым циклом и вы уничтожаете перф.
(Другая проблема заключается в том, что ваш код не создает новых потоков, как вам уже было указано)
Вам нужно использовать другой способ синхронизации: например, можно использовать CountDownLatch
.
Другое дело: нет необходимости порождать два новых потока при разделении рабочей нагрузки: порождать только один новый поток и делать вторую половину в текущем потоке.
Кроме того, вы, вероятно, не хотите создавать больше потоков, чем доступно ядер.
См. Мой вопрос здесь (просьба о хорошем многопоточном слиянии с открытым исходным кодом / quicksort / что угодно). Тот, который я использую, проприетарный, я не могу его вставить.
Многопоточная быстрая сортировка или слияние
Я не реализовал Mergesort, но QuickSort, и я могу вам сказать, что копирование массива не происходит.
Что я делаю, это:
- выбрать точку разворота
- обменять значения по мере необходимости
- мы достигли предела потока? (в зависимости от количества ядер)
- да: сортировать первую часть в этой теме
- no: создать новую тему
- сортировка второй части в текущей теме
- дождитесь окончания первой части, если это еще не сделано (с помощью CountDownLatch).
Код, порождающий новый поток и создающий CountDownLatch, может выглядеть так:
final CountDownLatch cdl = new CountDownLatch( 1 );
final Thread t = new Thread( new Runnable() {
public void run() {
quicksort(a, i+1, r );
cdl.countDown();
}
} };
Преимущество использования средств синхронизации, таких как CountDownLatch, заключается в том, что он очень эффективен и позволяет не тратить время на работу с низкоуровневыми особенностями синхронизации Java.
В вашем случае «сплит» может выглядеть так (не проверено, это просто для того, чтобы дать представление):
if ( threads.getAndIncrement() < 4 ) {
final CountDownLatch innerLatch = new CountDownLatch( 1 );
final Thread t = new Merger( innerLatch, b );
t.start();
mergeSort( a );
while ( innerLatch.getCount() > 0 ) {
try {
innerLatch.await( 1000, TimeUnit.SECONDS );
} catch ( InterruptedException e ) {
// Up to you to decide what to do here
}
}
} else {
mergeSort( a );
mergeSort( b );
}
(не забудьте "отсчитать" защелку после каждого слияния)
Где бы вы заменили количество потоков (до 4 здесь) на количество доступных ядер. Вы можете использовать следующее (один раз, скажем, для инициализации некоторой статической переменной в начале вашей программы: число ядер вряд ли изменится [если только вы не работаете на машине, разрешающей горячую перезагрузку ЦП, как позволяют некоторые системы Sun]):
Runtime.getRuntime().availableProcessors()