Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/385.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_Scala_Concurrency_Pool - Fatal编程技术网

Java 具有多个限制的线程池

Java 具有多个限制的线程池,java,multithreading,scala,concurrency,pool,Java,Multithreading,Scala,Concurrency,Pool,我想要一个线程池,提供最多X个线程来处理任务,到目前为止没有问题。但是,每个提交的任务都可以指定一个特定限制的IO目标(例如Y) 因此,提交的IOTask返回目标“google.com”,限制为4(Y),池的全局限制为16(X)。我想提交10个google.com-tasks,其中只有4个并行处理,池中有12个线程可供其他任务使用 我怎样才能做到这一点呢?嗯。。。很抱歉,现有的ExecutorService不允许这样精细的控制。您可能需要自己扩展ExecutorService类来添加此功能,或者

我想要一个线程池,提供最多X个线程来处理任务,到目前为止没有问题。但是,每个提交的任务都可以指定一个特定限制的IO目标(例如Y)

因此,提交的IOTask返回目标“google.com”,限制为4(Y),池的全局限制为16(X)。我想提交10个google.com-tasks,其中只有4个并行处理,池中有12个线程可供其他任务使用


我怎样才能做到这一点呢?

嗯。。。很抱歉,现有的
ExecutorService
不允许这样精细的控制。您可能需要自己扩展ExecutorService类来添加此功能,或者使用两个单独的固定线程池,一个容量为4,另一个容量为12。

实现此功能并不简单,因为每个目标都需要单独的队列(因此等待代码变得复杂得多),或者从一个队列中跳过已满负荷的目标(导致性能开销)。您可以尝试扩展ExecutorService来实现这一点,但扩展似乎并不简单

最新答案/解决方案:

仔细考虑后,阻塞问题的最简单解决方案是拥有阻塞队列(按照正常情况)和队列映射(每个目标一个队列,以及每个目标的可用线程计数)。队列映射仅用于已传递执行的任务(由于该目标已经运行了太多线程)从常规阻塞队列中获取任务后

因此,执行流程如下所示:

  • 通过调用代码提交任务(具有特定目标)
  • 任务被放入阻塞队列(可能在这里由您自己的包含目标信息的任务类包装)
  • 线程(来自线程池)正在阻塞队列上等待(通过take()
  • 线程接受提交的任务
  • 线程在锁上同步
  • 线程检查该目标的可用计数
  • 如果可用计数>0

    • 然后线程将计数减少1,释放锁,运行任务
    • 否则,线程将任务放入目标到任务队列的映射(此映射是传递给任务映射的),释放锁,然后返回到阻塞队列等待
  • 当线程完成任务的执行时,它将:

    • 在锁上同步
    • 检查刚刚执行的目标的计数
    • 如果计数=0
      • 然后检查传递的任务映射中是否存在此目标的任何任务(如果存在),然后释放锁并运行它
    • 如果计数不是0,或者传递的映射/队列上没有相同目标的任务,则增加可用计数(对于该目标),释放锁,然后返回到等待阻塞队列

  • 此解决方案避免了任何显著的性能开销,也避免了使用单独的线程来管理队列。

    这样做的一个想法是扩展ExecutorService,并且在您的类中有两个线程池,一个线程池的容量为4,另一个线程池的容量为12


    然后实现所需的方法,并根据提交的IOTasks,将任务定向到希望其进入的池。

    使用线程总数计数器和HashMap,计算当前尝试访问站点X的线程数。如果要启动新线程,请调用检查w的同步方法aits(while循环内的wait()),直到哈希映射中的线程数小于4且线程总数小于16。然后递增两个计数器并启动线程。当线程完成时,它应调用第二个同步方法,该方法递减计数器并调用notify()以更具体的方式思考一些答案

  • 您需要自己的BlockingQueue,它可以分离不同类型的任务,并根据内部计数器返回所需的Runnable

  • 扩展ThreadPoolExecutor并在执行前执行和执行后执行

  • 调用beforeExecute时,如果Runnable类型为X,则会增加队列中的计数器。调用afterExecute时,会减少该计数器

    在您的队列中,您将根据计数器的值返回相应的Runnable,我相信take方法就是您要执行此操作的地方


    这里有一些同步问题需要仔细考虑,以确保计数器永远不会超过4。不幸的是,一旦您在beforeExecute内,就太晚了,但是能够简单地知道在给定时间运行多少任务可能会让您开始执行。

    您可以包装两个ExecutorService实例在自定义类中,手动管理任务的提交,如下所示:

    class ExecutorWrapper {
    
        private ExecutorService ioExec = Executors.newFixedThreadPool(4);
        private ExecutorService genExec = Executors.newFixedThreadPool(12);
    
        public Future<?> submit(final IOTask task) {
            return ioExec.submit(task);
        }
    
        public Future<?> submit(final Runnable task) {
            return genExec.submit(task);
        }
    }
    
    interface IOTask extends Runnable {}
    
    类执行器包装器{
    私有ExecutorService ioExec=Executors.newFixedThreadPool(4);
    private ExecutorService genExec=Executors.newFixedThreadPool(12);
    公共未来提交(最终任务){
    返回ioExec.submit(任务);
    }
    公共未来提交(最终可运行任务){
    返回genExec.submit(任务);
    }
    }
    接口IOTask扩展可运行{}
    

    这允许您使用4个线程来执行IO操作,剩下12个线程来执行其他任务。

    但这意味着,在线程可用之前,尝试启动新线程的操作本身会被等待/通知阻止。我想尽量减少使用的线程数。是的,您是对的。这是一个错误的答案。I w如果可以的话,我会投反对票。这就是我基本上求助的方法。