[英]Multi - threading
我试图在Java中创建一个并行的快速排序,我认为这是一个幼稚的操作(因为我还没有研究Interface Executor等)
我需要一种方法来在所有线程完成后打印排序后的数组..但是我不知道我要预先拥有多少个线程..所以我这样做的方式是每次递归等待使用join()方法..因此,被调用的第一个join方法必须等待,直到所有其他线程都完成了。
这样,当我在打印数组的main()中执行最后两行时,我可以确定所有线程都已完成...
所以我有两个问题..
这是一个并行运行的多线程程序,对吗? 还是我犯了一些错误,说它实际上是一个接一个的线性运行?
我在主方法中显示排序数组的解决方案是否正确?
这是我的代码:
public class Main {
public static void main(String[] args) {
ArrayList<Integer> array = new ArrayList();
//please assume that I have invoked the input for the array from the user
QuickSortWithThreads obj = new QuickSortWithThreads(array,0 ,array.size()-1 );
for(int i = 0; i < array.size(); i++)
System.out.println(array.get(i));
}
}
public class QuickSortWithThreads {
public QuickSortWithThreads(ArrayList <Integer> arr, int left, int right){
quicksort(arr, left, right);
}
static void quicksort(ArrayList <Integer> arr, int left, int right) {
int pivot;
if(left<right){
pivot = partition(arr, left, right);
QuickSortThread threadLeftSide = new QuickSortThread(arr, pivot + 1, right);
threadLeftSide.start();
quicksort(arr, left, pivot - 1);
try {
threadLeftSide.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
static int partition(ArrayList<Integer> arr, int left, int right) {
int pivot = arr.get(right);
int i = left -1;
for( int j = left; j <= right - 1; j++) {
if (arr.get(j) <= pivot){
i = i + 1;
exchange(arr, i, j);
}
}
exchange(arr, i + 1, right);
return i + 1;
}
static void exchange(ArrayList<Integer> arr, int i, int j) {
int swap = arr.get(i);
arr.set(i, arr.get(j));
arr.set(j, swap);
}
private static class QuickSortThread extends Thread {
int right;
int left;
ArrayList<Integer> refArray;
public QuickSortThread(ArrayList<Integer> array, int left, int right) {
this.right = right;
this.left = left;
refArray = new ArrayList<Integer>();
refArray = array;
}
public void run() {
quicksort(refArray, left, right);
}
}
}
如果我们知道线程的总数,则可以使用以线程数初始化的CountDownLatch 。 但是由于我们不知道线程数,我们需要一个扩展的CountDownLatch,它允许在创建计数器后增加它。 不幸的是,由于基础计数器是私有的,我们不能仅扩展CountDownLatch类。 一种方法是复制CountDownLatch的原始代码以访问基础计数器。 较不冗长的方法是扩展信号量,以获取对reducePermits
方法的访问,就像在Reduceable Semaphore中所做的那样 。 原则上,CountDownLatch和Semaphore是相似的工具,但内部计数器的解释有所不同:前者计数否决权,后者计数许可。
整个想法是在方法run()
的末尾减少创建或启动线程时的许可数量,并在线程完成时释放许可。 许可的初始数量为1,因此,如果没有启动线程,则主过程可以自由完成。 请注意,在方法run()
的开头减少许可的数量为时已晚。
为了获得真正好的工作代码,您还需要使用具有固定线程数的线程池,并对小型数组进行串行排序。
是的,您的代码可以并行运行。 结果打印看起来也不错。
一个问题是您创建了大量的线程:在最低级别,您将拥有与列表元素几乎一样多的线程。 而且您不会捕获由此产生的异常,因此您不会(在您的主线程中)知道这没有按预期进行。
您可能应该限制增加新线程的级别数。 一旦您通过了说3个级别,您将拥有大约2 3 = 8个线程,这应该足以使大多数核心在最合理的计算机上忙碌。 然后,您可以继续其余的计算而无需分支其他线程。 您可以通过将附加的参数branching
传递给quicksort
方法来实现。 在QuickSortWithThreads
构造函数的调用中将其设置为3
,并在每次调用时将其递减。 一旦计数达到0
不要分支。 这将给您以下电话:
quicksort(3, …)
quicksort(2, …)
quicksort(1, …)
quicksort(0, …)
quicksort(0, …)
quicksort(1, …)
quicksort(0, …)
quicksort(0, …)
quicksort(2, …)
quicksort(1, …)
quicksort(0, …)
quicksort(0, …)
quicksort(1, …)
quicksort(0, …)
quicksort(0, …)
由于每个非叶子调用与其子对象共享一个线程,因此您可以从上述叶子数量中减去最多8个线程。
作为这种限制线程数的自制方法的替代方法,您当然可以使用您提到的Executor
接口来Executor
此操作。 您可以创建一个ThreadPoolExecutor
来管理您的线程,并将每个递归调用作为Runnable
的实例传递(该外观可能类似于QuickSortThread
)。 这种方法的一个主要问题是检测终止。 尤其是要避免出现错误时发生死锁。 因此,最好改用ForkJoinTask
,因为在这种情况下,您可以让每个任务等待其他子项的结束,这与您编写的内容非常相似,并且您仍然可以限制关联的ForkJoinPool
的实际线程数。 如果您没有返回值,那么实际的实现将最好使用RecursiveAction
,这是ForkJoinTask
,其文档中包含与您的场景非常相似的示例。
线程的行为方式取决于硬件。 使用单核CPU且没有超线程,计算机一次循环地逐个线程处理一个线程。 如果您具有超线程和/或多个内核,它们可以同时运行多行。 对examplethread.join()的调用使调用线程等待,直到examplethread完成其工作(通过从run()方法返回)。 如果您创建一个线程,然后在两行之后调用join,则您将拥有多线程同步任务,这与使它成为单线程非常相似。
id建议创建一个ArrayList并将每个线程添加到列表中,所有线程都设置好并工作后,您可以调用
for(Thread t : mythreadlist) {
try {
t.join();
} catch (InterruptedException e) { System.err.println("Interrupted Thread"); }
}
使您的应用程序等待所有线程退出。
编辑:
// [...]
public class QuickSortWithThreads {
ArrayList<QuickSortThread> threads = new ArrayList<>();
public QuickSortWithThreads(ArrayList <Integer> arr, int left, int right){
quicksort(arr, left, right); // Pretty much make your threads start their jobs
for(Thread t : threads) { // Then wait them to leave.
try {
t.join();
} catch (InterruptedException e) { System.err.println("Interrupted Thread"); }
}
}
// [...]
static void quicksort(ArrayList <Integer> arr, int left, int right) {
int pivot;
if(left<right){
pivot = partition(arr, left, right);
QuickSortThread threadLeftSide = new QuickSortThread(arr, pivot + 1, right);
threadLeftSide.start();
threads.add(threadLeftSide());
//
quicksort(arr, left, pivot - 1);
}
}
// [...]
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.