我试图用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
get是一种计算方法时,它将把它分配给所有线程并等待它们完成。
我还没有使用等待的线程,所以一旦完成线程的计数器等于总线程数,我尝试保存当前线程并继续执行。
这对我来说似乎很合逻辑,但是我的代码存在多个问题:
IllegalMonitorStateException
。ComputationThreads
将进入无限循环并等待直到给出新任务。 (也许也可以通过让他们等待来完成)我不想每次给出新任务时都创建一个新线程,并在任务完成后销毁它们。
答案 0 :(得分:0)
我认为您不需要线程之间的所有信令。您可以只使用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();
}
}
答案 1 :(得分:0)
这是如何使用软件包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 expression来简化上述代码:
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秒钟),但通常您也可以
Future
是否已通过方法get(long timeout, TimeUnit unit)
完成cancel()
它isDone()
和isCancelled()
检查任务是完成还是取消希望这会有所帮助!