Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/387.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 将其他任务排入队列的任务使用哪种线程机制?_Java_Multithreading_Concurrency_Executorservice - Fatal编程技术网

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");