如何将任务分配给Java线程?

时间:2018-02-20 11:58:08

标签: java multithreading

我有一个名为jobs的数据库表和一些将数据插入此表的生产者服务。我需要创建一个消费者服务来处理这些数据。

我有一个8核16线程的服务器,我创建了一个包含16个线程的线程池。

ExecutorService executorService = Executors.newFixedThreadPool(16);

我将从数据库中获取16条记录,并将此数据分发给用户线程。在所有线程完成工作后,我将获取另外16条记录。(我真的不知道我的解决方案是否有效)

如何将这些任务分配给消费者线程?我需要使用BlockingQueue吗?

5 个答案:

答案 0 :(得分:2)

ExecutorService的javadoc:s在这里可能会派上用场。 将工作创建为实施Callable,将它们放入集合中并使用executorService.invokeAll(<Collection of Callable),检查要完成的期货。 或者只使用executorService.submit(<task>)

答案 1 :(得分:2)

没有必要批量生产&#34;记录。只需将它们提交给执行者服务。

如果您担心通过填充执行程序服务的队列可能会压倒JVM的堆,那么使用(例如)ArrayBlockingQueue作为工作队列来创建执行程序服务。如果工作队列太长,这将导致执行程序拒绝请求。各种其他策略都是可能的。

如果您打算使用ExecutorService做一些奇特的事情,我建议您阅读ThreadPoolExecutor from rest_framework.permissions import IsAuthenticatedOrReadOnly class TestApiView(APIView): permission_classes = (IsAuthenticatedOrReadOnly,) def get(self, request): return Response({'text': 'allow any'}) def post(self, request): return Response({'text': 'IsAuthenticated'}) 。 API既丰富又复杂,在您选择特定的实施方法之前,需要仔细阅读。

答案 2 :(得分:2)

当线程不可用时,Executor服务有一个缓冲任务的队列。 您需要编写另一个线程,该线程将定期向执行程序服务提交任务,并检查完成策略。如果执行者服务队列是完全需要处理的话。

答案 3 :(得分:1)

虽然这种方法可行,但它可能仍会留下一些未使用的计算能力,假设16是最佳线程数。

我宁愿使用基于拉的方法,其中线程“拉”条目来处理:

选项1 :检索所有记录并使用并行流:

List<Record> allValues = //fetch
allValues.parallelStream().forEach(...do your processing...);

//You can even have a better version that reads data from the result set as needed:
Stream.generate(() -> {
    resultSet.next();
    return rs.getObject(1); //Read/create the value from the record
});

选项2 :根据从数据库中检索到的所有数据使用某种类型的队列,然后创建处理队列的可调用实现(它们循环,每个线程保持忙,直到队列耗尽)。然后,您可以使用执行程序服务,启动这些任务:

Queue<Object> records; //Create the queue of records
ExecutorService es; // Instantiate the executor service with desired capacity

//Execute the runnable that processes the queue. Only ending when there's nothing on the queue.
for (int i = 0; i < 16; i++) {
    es.execute(() -> {
        while(!records.isEmpty()) { 
            //You need to handle this as this 
            //check and a poll() call may need to be synced.

            Object next = records.poll();
            //process
        }
    });
}

答案 4 :(得分:1)

我建议使用大小为可用处理器数量两倍的专用ArrayBlockingQueue(在您的情况下为2x16 = 32)。

一个单独的线程从数据库中读取记录并将它们放入队列中。如果队列已满,则该线程将在下一个记录的空间可用时等待。如果处理记录的速度快于读取线程能够从数据库中检索记录,则可以使用许多读取线程,所有读取线程都运行相同的读取循环。

消费者线程只需从队列中获取下一条记录并在循环中处理它们。

附加: 替代方法是使用类型为Runnable(或Callable)的对象包装每个记录,并将其提交给执行程序服务。一个较小的缺点是必须创建额外的包装器对象。更大的缺点是输入执行程序队列可能会过载。然后,根据使用的队列类型,该队列要么抛出RejectedExecutionException,要么消耗所有可用的核心内存。溢出时ArrayBlockingQueue只是暂停生产者线程。