Java 将其他任务排入队列的任务使用哪种线程机制?
我正在使用创建其他任务的任务。这些任务可能会也可能不会创建后续任务。我事先不知道总共会创建多少任务。在某个时刻,不再创建任何任务,所有任务都将完成 当最后一项任务完成时,我必须做一些额外的事情 应该使用哪种线程机制?我读过,但似乎没有一本合适 我也尝试过使用,但我遇到了一些问题,例如最后无法执行某些操作,您可以在下面看到我的尝试:Java 将其他任务排入队列的任务使用哪种线程机制?,java,multithreading,concurrency,executorservice,Java,Multithreading,Concurrency,Executorservice,我正在使用创建其他任务的任务。这些任务可能会也可能不会创建后续任务。我事先不知道总共会创建多少任务。在某个时刻,不再创建任何任务,所有任务都将完成 当最后一项任务完成时,我必须做一些额外的事情 应该使用哪种线程机制?我读过,但似乎没有一本合适 我也尝试过使用,但我遇到了一些问题,例如最后无法执行某些操作,您可以在下面看到我的尝试: import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicInt
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
public class Issue {
public static void main(String[] args) throws InterruptedException {
var count = new AtomicInteger(1);
var executor = Executors.newFixedThreadPool(3);
class Task implements Runnable {
final int id = count.getAndIncrement();
@Override
public void run() {
try {
MILLISECONDS.sleep((long)(Math.random() * 1000L + 1000L));
} catch (InterruptedException e) {
// Do nothing
}
if (id < 5) {
executor.submit(new Task());
executor.submit(new Task());
}
System.out.println(id);
}
}
executor.execute(new Task());
executor.shutdown();
// executor.awaitTermination(20, TimeUnit.SECONDS);
System.out.println("Hello");
}
}
哪种线程机制可以帮助我做到这一点?这似乎相当棘手。如果甚至有一个任务在队列中或当前正在执行,那么由于您无法确定它是否会生成另一个任务,因此您无法知道它可以运行多长时间。这可能是一系列任务的开始,这些任务需要另外两个小时 我认为实现这一点所需的所有信息都由executor实现封装。您需要知道正在运行的内容和队列中的内容 我认为你很不幸地看到不得不写你自己的遗嘱执行人。它不需要复杂,如果您不希望它符合JDK的接口,它也不必符合JDK的接口。只是用来维护线程池和任务队列的东西。添加将侦听器附加到执行器的功能。当队列为空且没有正在执行的任务时,您可以通知侦听器 下面是一个快速代码草图
class MyExecutor
{
private final AtomicLong taskId = new AtomicLong();
private final Map<Long, Runnable> idToQueuedTask = new ConcurrentHashMap<>();
private final AtomicLong runningTasks = new AtomicLong();
private final ExecutorService delegate = Executors.newFixedThreadPool(3);
public void submit(Runnable task) {
long id = taskId.incrementAndGet();
final Runnable wrapped = () -> {
taskStarted(id);
try {
task.run();
}
finally {
taskEnded();
}
};
idToQueuedTask.put(id, wrapped);
delegate.submit(wrapped);
}
private void taskStarted(long id) {
idToQueuedTask.remove(id);
runningTasks.incrementAndGet();
}
private void taskEnded() {
final long numRunning = runningTasks.decrementAndGet();
if (numRunning == 0 && idToQueuedTask.isEmpty()) {
System.out.println("Done, time to notify listeners");
}
}
public static void main(String[] args) {
MyExecutor executor = new MyExecutor();
executor.submit(() -> {
System.out.println("Parent task");
try {
Thread.sleep(1000);
}
catch (Exception e) {}
executor.submit(() -> {
System.out.println("Child task");
});
});
}
}
类MyExecutor
{
private final AtomicLong taskId=new AtomicLong();
私有最终映射idToQueuedTask=新的ConcurrentHashMap();
private final AtomicLong runningTasks=new AtomicLong();
私有最终ExecutorService委托=Executors.newFixedThreadPool(3);
公共作废提交(可运行任务){
long id=taskId.incrementAndGet();
最终可运行包装=()->{
任务启动(id);
试一试{
task.run();
}
最后{
taskend();
}
};
idToQueuedTask.put(id,包装);
委托。提交(包装);
}
已启动专用void任务(长id){
idToQueuedTask.remove(id);
runningTasks.incrementAndGet();
}
私有void taskend(){
final long numRunning=runningTasks.decrementAndGet();
if(numRunning==0&&idToQueuedTask.isEmpty()){
System.out.println(“完成,通知侦听器的时间”);
}
}
公共静态void main(字符串[]args){
MyExecutor executor=新的MyExecutor();
执行人提交(()->{
System.out.println(“父任务”);
试一试{
睡眠(1000);
}
捕获(例外e){}
执行人提交(()->{
System.out.println(“子任务”);
});
});
}
}
如果您将Executor Service
更改为:
ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(3);
然后可以使用计数函数等待:
while(executor.getTaskCount() > executor.getCompletedTaskCount())
{
TimeUnit.SECONDS.sleep(10L);
}
executor.shutdown();
System.out.println("Hello");
如果您需要一个接一个地执行任务以获得总的顺序(没有任何并行化),请看这个如果您正在寻找更灵活的方法来并行化任务,您可能会加强前面的示例,或者看看ForkJoinPool@AnatolyG,我不想要顺序:只有多线程,我用标记“multi-threading”和“并发性”。此外,ForkJoinPool并不能解决这个问题。我认为一个好的开始可能是定义“最后一个”任务的确切含义?正式规则是什么?@解析相关部分不是“最后一个”,而是整个介词:“当最后一个任务完成时”:这基本上意味着每个“
任务
“已完成自己的语句的执行,队列中不再有Task
。”队列中不再有Task”-恐怕还不够。例如,您将在一个循环中计划/执行10个任务,有时新任务的执行速度比您将下一个任务放入队列的速度快。您让队列空了几微秒,但我们刚刚执行的任务并不是真正的“最后”任务,对吗?“超时后队列中不再有任务”可能会起作用,但似乎也很奇怪。。。我们能确定何时停止添加吗?我有点想避免这种情况,希望JDK中有一个现有的框架,但如果没有,那就这样吧@OlivierGrégoire在写这篇文章后我意识到,如果你包装了任务,那么你可以将几乎所有的任务委托给现有的ExecutorService,请参见编辑。这不是我所期望的,但它几乎解决了我过去一周一直在努力解决的所有问题。回想起来,这似乎很简单!谢谢:)很多事情都不是很好。未经检查的强制转换,这两种方法的文档都说“返回值只是一个近似值”,线程被阻塞的事实(值越小,浪费的周期就越多。值越大,完成和运行终结器代码之间的差距就越大)@Michael是的,我知道。未经检查的强制转换可以由直接调用Exceutors.newFixedThreadPool
方法的内容替换。计数方法是一种近似方法,因为可以从外部添加任务。在这种情况下,它没有那么重要,因为新任务被添加到活动任务中。
while(executor.getTaskCount() > executor.getCompletedTaskCount())
{
TimeUnit.SECONDS.sleep(10L);
}
executor.shutdown();
System.out.println("Hello");