请耐心等待我,因为我在多线程编程方面并不是非常精明......
我目前正在构建一个使用ThreadPool ExecutorService进行各种runnable的系统。这很简单。但是,我正在考虑让runnables自己根据原始runnable中发生的事情产生额外的runnable的可能性(即,如果成功,请执行此操作,如果失败,请执行此操作等,因为某些任务必须先完成其他任务执行)。应该注意的是,主线程不需要被告知这些任务的结果,尽管它可能很方便处理异常,即,如果无法联系外部服务并且所有线程都因此抛出异常,那么停止提交任务并定期检查外部服务,直到它恢复。这不是完全必要的,但它会很好。
即,提交任务A.任务A做了一些事情。如果一切顺利,任务A将执行任务B.如果某些内容无法正常运行或抛出异常,请执行任务C.每个子任务也可能有其他任务,但只有几个级别。我宁愿做一些像这样的事情而不是单一任务中的大型咆哮条件,因为这种方法可以提供更大的灵活性。
但是,我不确定这会如何影响线程池。我假设从池中的线程中创建的任何其他线程将存在于池外部,因为它们本身未直接提交到池中。这是正确的假设吗?如果是这样,那可能是一个坏主意(好吧,如果没有,它可能不是一个非常好的主意),因为它可能会导致更多的线程,因为原始线程完成并且提交了一个新任务,同时线程产生于之前的任务仍在继续(并且可能比其他任务持续更长时间)。
我还考虑将这些实现为Callables,并在Future中放置一个响应对象,然后根据响应将相应的Callable添加到线程池中。但是,这会将所有操作都绑定回主线程,这似乎是一个不必要的瓶颈。我想我可以将Runnable放入池中,该池本身处理Callable和后续操作的执行,但之后我获得了两倍的线程。
我是在正确的轨道上还是完全不在轨道上?
答案 0 :(得分:0)
答案 1 :(得分:0)
有很多方法可以做你想要的。你需要小心,不要创建太多的线程。
以下是一个示例,您可以使用ExecutorCompletionService提高效率,也可以使用Runnable。
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class ThreadsMakeThreads {
public static void main(String[] args) {
new ThreadsMakeThreads().start();
}
public void start() {
//Create resources
ExecutorService threadPool = Executors.newCachedThreadPool();
Random random = new Random(System.currentTimeMillis());
int numberOfThreads = 5;
//Prepare threads
ArrayList<Leader> leaders = new ArrayList<Leader>();
for(int i=0; i < numberOfThreads; i++) {
leaders.add(new Leader(threadPool, random));
}
//Get the results
try {
List<Future<Integer>> results = threadPool.invokeAll(leaders);
for(Future<Integer> result : results) {
System.out.println("Result is " + result.get());
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
threadPool.shutdown();
}
class Leader implements Callable<Integer> {
private ExecutorService threadPool;
private Random random;
public Leader(ExecutorService threadPool, Random random) {
this.threadPool = threadPool;
this.random = random;
}
@Override
public Integer call() throws Exception {
int numberOfWorkers = random.nextInt(10);
ArrayList<Worker> workers = new ArrayList<Worker>();
for(int i=0; i < numberOfWorkers; i++) {
workers.add(new Worker(random));
}
List<Future<Integer>> tasks = threadPool.invokeAll(workers);
int result = 0;
for(Future<Integer> task : tasks) {
result += task.get();
}
return result;
}
}
class Worker implements Callable<Integer> {
private Random random;
public Worker(Random random) {
this.random = random;
}
@Override
public Integer call() throws Exception {
return random.nextInt(100);
}
}
}
答案 2 :(得分:0)
从其他任务向线程池提交任务是非常有意义的想法。但我担心你会想到在不同的线程上运行新任务,这真的可以占用所有的内存。只需在创建池时设置线程数限制,并将新任务提交到该线程池。
这种方法可以在不同的方向进一步阐述。首先,使用接口方法将任务视为普通对象,并让该方法决定是否要将此对象提交给线程池。这要求每个任务都知道其线程池 - 在创建时将其作为参数传递。更方便的是,继续引用线程池作为线程局部变量。
您可以轻松模拟函数式编程:对象表示函数调用,对于每个参数,它都有相应的set方法。设置所有参数后,该对象将提交给线程池。
另一个方向是actor编程:task类有单集方法,但可以多次调用,如果还没有处理上一个参数,set方法不会将任务提交给线程池,而只是存储它队列中的参数。 run()方法处理队列中的所有可用参数,然后返回。
所有这些功能都在数据流库https://github.com/rfqu/df4j中实现。我故意写它来支持基于任务的并行性。