我编写了一个简单的程序,旨在启动一些线程。然后线程应该从整数数组中选择一个整数 n ,用它来等待 n 并返回线程等待的时间 t 结果数组。
如果一个线程完成了它的任务,它应该选择下一个尚未分配给另一个线程的线程。
当然:必须维护数组中的顺序,以便整数和结果匹配。
据我所知,我的代码运行顺畅。 但是我使用了一行特别不满意的代码块,并希望有一个很好的方法来解决这个问题,而不会改变太多:
while(Thread.activeCount() != 1); // first evil line
我有点滥用这一行,以确保在我使用结果访问我的数组之前,我的所有线程都完成了所有任务。我想这样做是为了防止错误的值,比如0.0,Null Pointer Exception ...等等(简而言之,任何会导致应用程序出现实际使用崩溃的东西) 任何形式的建设性帮助表示赞赏。我也不确定,如果我的代码仍能在线程的非常长的任务数组中顺利运行,例如结果不再与整数的顺序匹配。
任何建设性的帮助表示赞赏。
头等舱:
public class ThreadArrayWriterTest {
int[] repitions;
int len = 0;
double[] timeConsumed;
public boolean finished() {
synchronized (repitions) {
return len <= 0;
}
}
public ThreadArrayWriterTest(int[] repitions) {
this.repitions = repitions;
this.len = repitions.length;
timeConsumed = new double[this.len];
}
public double[] returnTimes(int[] repititions, int numOfThreads, TimeConsumer timeConsumer) {
for (int i = 0; i < numOfThreads; i++) {
new Thread() {
public void run() {
while (!finished()) {
len--;
timeConsumed[len] = timeConsumer.returnTimeConsumed(repititions[len]);
}
}
}.start();
}
while (Thread.activeCount() != 1) // first evil line
;
return timeConsumed;
}
public static void main(String[] args) {
long begin = System.currentTimeMillis();
int[] repitions = { 3, 1, 3, 1, 2, 1, 3, 3, 3 };
int numberOfThreads = 10;
ThreadArrayWriterTest t = new ThreadArrayWriterTest(repitions);
double[] times = t.returnTimes(repitions, numberOfThreads, new TimeConsumer());
for (double d : times) {
System.out.println(d);
}
long end = System.currentTimeMillis();
System.out.println("Total time of execution: " + (end - begin));
}
}
第二课:
public class TimeConsumer {
double returnTimeConsumed(int repitions) {
long before = System.currentTimeMillis();
for (int i = 0; i < repitions; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
long after = System.currentTimeMillis();
double ret = after - before;
System.out.println("It takes: " + ret + "ms" + " for " + repitions + " runs through the for-loop");
return ret;
}
}
答案 0 :(得分:1)
等待所有线程完成的最简单方法是保留它们的集合,然后依次为每个线程调用Thread.join()
。
答案 1 :(得分:1)
除了.join()
,您还可以使用ExecutorService来管理线程池,
Executor,提供管理终止和方法的方法 这可以产生一个跟踪一个或多个进度的Future 异步任务。
可以关闭ExecutorService,这将导致它拒绝新的 任务。提供了两种不同的方法来关闭 ExecutorService的。 shutdown()方法将允许先前提交 在终止之前执行的任务,而shutdownNow()方法 阻止等待任务启动并尝试暂停 执行任务。终止后,执行者没有主动执行任务 执行,没有等待执行的任务,也没有新的任务 提交。应该关闭一个未使用的ExecutorService以允许 回收其资源。
方法提交扩展基本方法Executor.execute(Runnable) 创建并返回可用于取消执行的Future 和/或等待完成。方法invokeAny和invokeAll执行 最常用的批量执行形式,执行a 收集任务,然后等待至少一个或全部 完整。
ExecutorService executorService = Executors.newFixedThreadPool(maximumNumberOfThreads);
CompletionService completionService = new ExecutorCompletionService(executorService);
for (int i = 0; i < numberOfTasks; ++i) {
completionService.take();
}
executorService.shutdown();
另外,请查看ThreadPoolExecutor
答案 2 :(得分:0)
由于java提供了带有concurrent包的更高级的线程API,您应该查看ExecutorService,这简化了线程管理机制。
简单解决您的问题。
使用Executors API创建线程池
static ExecutorService newFixedThreadPool(int nThreads)
创建一个线程池,该线程池重用在共享的无界队列中运行的固定数量的线程。
使用invokeAll等待所有任务完成。
示例代码:
ExecutorService service = Executors.newFixedThreadPool(10);
List<MyCallable> futureList = new ArrayList<MyCallable>();
for ( int i=0; i<12; i++){
MyCallable myCallable = new MyCallable((long)i);
futureList.add(myCallable);
}
System.out.println("Start");
try{
List<Future<Long>> futures = service.invokeAll(futureList);
for(Future<Long> future : futures){
try{
System.out.println("future.isDone = " + future.isDone());
System.out.println("future: call ="+future.get());
}
catch(Exception err1){
err1.printStackTrace();
}
}
}catch(Exception err){
err.printStackTrace();
}
service.shutdown();
有关实现相同的更多详细信息,请参阅此相关的SE问题: