[英]How to let a thread wait until task is finished?
我试图在java中编写一些线性代数库,并希望使用CPU实现多线程。 为此,我创建了一个ComputationMaster
类,它有8个ComputationThread
。
这个想法是,当作为任务给予主时,它将把这个任务交给所有的线程,他们将在那里工作。
我的尝试如下:
任务是一种被调用的方法,直到它返回false
。 该方法本身需要管理它正在工作的数据,但这不是问题本身的一部分。
public interface ComputationMethod {
public boolean execute();
}
现在,我们来谈谈ComputationThread:它扩展了Thread
,看起来像这样:
ComputationMethod computation;
public ComputationThread(){
super();
this.start();
}
public void run(){
while(!this.isInterrupted()){
try{
if(computation != null){
while(computation.execute()){}
computation = null;
ComputationMaster.notify_thread_finished();
}
}catch (Exception e){
e.printStackTrace();
this.interrupt();
}
}
this.interrupt();
}
您可以看到它通知ComputationMaster他完成了任务,因为任务本身返回false
。
最后,我将向您展示我对ComputationMaster
尝试:
public static final int MAX_THREAD_AMOUNT = 8;
public static Thread MAIN_THREAD;
private static ComputationThread[] threads = new ComputationThread[MAX_THREAD_AMOUNT];
static int finished = 0;
static synchronized void notify_thread_finished(){
finished ++;
if(finished == MAX_THREAD_AMOUNT){
MAIN_THREAD.notifyAll();
finished = 0;
}
}
public static void compute(ComputationMethod method){
for(ComputationThread t:threads){
t.computation = method;
}
MAIN_THREAD = Thread.currentThread();
try {
MAIN_THREAD.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
我们的想法是,当ComputationMaster
得到一个计算方法时,它会将它提供给所有线程并等到它们完成。 我还没有等待Threads,所以我尝试保存当前的Thread并让它继续,一旦完成的Threads的计数器等于总线程的数量。
这对我来说似乎很合乎逻辑,但我的代码存在多个问题:
IllegalMonitorStateException
。 ComputationThreads
将进入无限循环并等待直到给出新任务。 (也许这也可以让他们等待) 我不希望每次给出新任务时都创建一个新线程,并在任务完成后销毁它们。
我不认为你需要线程之间的所有信号。 你可以使用thread.join
另外,一个小的设计缺陷是,在设置computation
成员之前,线程处于无限旋转循环中。 这会降低您的初始性能。 您应该在启动线程之前设置computation
成员。 也就是说,不要让ComputationThread
的构造函数调用thread.start
。 在您的compute
功能中执行此操作。
这可能是你寻求的:
public static void compute(ComputationMethod method){
for(ComputationThread t:threads){
t.computation = method;
t.start();
}
// wait for all threads to finish
for(ComputationThread t:threads){
t.join();
}
}
然后您的运行功能简化为:
public void run(){
try {
while(computation.execute()){}
}
catch (Exception e){
e.printStackTrace();
}
}
这是一个如何使用包java.util.concurrent
来实现目标的示例。 首先,您要构建ExecutorService
:
ExecutorService svc = Executors.newFixedThreadPool(10);
在这里,我使用一个依赖于固定数量的线程来执行任务; 向其提交任务的基本方法是execute()
:
svc.execute(new Runnable() {
@Override
public void run() {
System.out.println("Hello!");
}
});
但是,这有一个限制,即被调用的方法没有返回值,而是需要返回一个Boolean
。 为此,请调用submit()
:
Future<Boolean> submit = svc.submit(new Callable<Boolean>() {
@Override
public Boolean call() {
return true;
}
});
使用Java 8引入的lambda表达式可以简化上面的代码:
Future<Boolean> submit = svc.submit(() -> true);
这是一个总结:
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.stream.Collectors;
public class Threads {
private final ExecutorService svc;
public Threads(ExecutorService svc) {
this.svc = svc;
}
public List<Future<Boolean>> execute(List<ComputationMethod> methods) throws InterruptedException {
return svc.invokeAll(methods.stream()
.map(im -> (Callable<Boolean>) im::execute)
.collect(Collectors.toList()));
}
}
这里有几点说明:
invokeAll()
而不是submit()
因为我需要处理一个计算列表,而不仅仅是一个 ComputationMethod
列表转换为Callable
列表 Callable
创建一个匿名类,而是使用lambda表达式 这是一个如何使用上述类的示例:
import org.junit.Test;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.*;
import java.util.stream.Collectors;
import static org.junit.Assert.assertEquals;
public class ThreadsTest {
@Test
public void runThreeCalculations() throws InterruptedException {
ExecutorService svc = Executors.newFixedThreadPool(10);
Threads threads = new Threads(svc);
List<Future<Boolean>> executions = threads.execute(Arrays.asList(
() -> true,
() -> true,
() -> true
));
svc.shutdown();
svc.awaitTermination(10, TimeUnit.SECONDS);
List<Boolean> results = executions.stream().map(
f -> {
try {
return f.get();
} catch (InterruptedException | ExecutionException e) {
throw new AssertionError(e);
}
}
).collect(Collectors.toList());
assertEquals(Arrays.asList(true, true, true), results);
}
}
在这个使用JUnit测试框架的自包含示例中,我在使用它之后关闭了Executor
并等待它关闭:
svc.shutdown();
svc.awaitTermination(10, TimeUnit.SECONDS);
但是,在生产情况下,您可能希望保持执行程序能够继续处理任务。
关于Future
的使用,它本身应该得到一个完整的帖子,这是你需要知道的开始:它代表了一个可能在未来结束的计算,你有办法尝试从中提取计算出的值。
在上面的例子中,我使用get()
来做到这一点,但只是因为我确定任务已经结束(因为我关闭了执行程序,并在出现问题/挂起时等了10秒),但一般情况下你可以也
get(long timeout, TimeUnit unit)
检查Future
是否已完成get(long timeout, TimeUnit unit)
cancel()
它 isDone()
和isCancelled()
检查任务是否完成或取消 希望这可以帮助!
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.