简体   繁体   English

修复并动态调整工作线程数

[英]Fixing and dynamically adjusting the number of worker-threads

I'm currently implementing a data container / data structure that has a addRecord(final Redcord record) and a close() method. 我目前正在实现一个数据容器/数据结构,它具有addRecord(最终Redcord记录)和close()方法。

public class Container{

    Container() {
    }

    public void addRecord(final Record record){
        // do something with the record
        // add the modified it to the container
    }

    public void close(){

    }     
}

Since in order to store a record to the container several steps need to be done, I want to outsource these steps to threads before finally adding them to the container. 因为为了将记录存储到容器需要完成几个步骤,我想在将这些步骤最终添加到容器之前将这些步骤外包给线程。

My problem is that I don't want any container instance to use more than let's say 4 threads and that if I have several container instances open at the same time in total not more than, eg, 12 threads should be used. 我的问题是我不希望任何容器实例使用超过4个线程,如果我有几个容器实例同时打开不超过,例如,应该使用12个线程。 In addition to that I'd like that once I create the 4th container each of the other 3 open containers should lose one of their threads, such that all 4 containers can use 3 threads each. 除此之外,我希望一旦我创建了第4个容器,其他3个打开的容器中的每一个都应该丢失其中一个线程,这样所有4个容器都可以使用3个线程。

I was looking into ThreadPools and ExecutorServices. 我正在研究ThreadPools和ExecutorServices。 While settings up a threadpool of size 12 is simple, it is hard to restrict the number of used threads to 4 when making use of ExecutorServices. 虽然设置大小为12的线程池很简单,但在使用ExecutorServices时很难将使用过的线程数限制为4。 Recall I want at most 12 threads, that's why I have a static instance of size 12 that I'm sharing between Container instances. 回想一下,我想要最多12个线程,这就是为什么我有一个大小为12的静态实例,我在Container实例之间共享。

This is what I started with 这就是我的开始

public class Container{

    public static final ExecutorService SERVICE= Executors.newFixedThreadPool(12);

    final List<Future<Entry>> _fRecords = new LinkedList<>();

    Container() {
    }

    public void addRecord(final Record record){
        _fRecords.add(SERVICE.submit(new Callable<Entry>{

            @Override
            public Entry call() throws Exception {
                return record.createEntry();
            }
        }));
    }

    public void close() throws Exception {
        for(final Future<Entry> f) {
            f.get(); // and add the entry to the container           
        }
    }     
}

Clearly, this will not ensure that at most 4 threads are used by a single instances. 显然,这不能确保单个实例最多使用4个线程。 Futhermore, I don't see how these Executorservices can be forced to shutdown workers or reassign them, if another container requires a couple of threads. 此外,我不知道如果另一个容器需要几个线程,这些Executorservices如何被强制关闭工作人员或重新分配它们。

Questions: 问题:

  1. Since Redord itself is an interface and the running time of createEntry() depends on the actual implementation, I want to ensure that different containers don't use the same threads/workers, ie, I don't want that any thread does the work for two different containers. 由于Redord本身是一个接口,并且createEntry()的运行时间取决于实际的实现,我想确保不同的容器不使用相同的线程/ worker,即我不希望任何线程完成工作两个不同的容器。 The idea behind this is that I don't want that a container instance that only processes "long running" records is slowed down by containers that only deal with "short running records". 这背后的想法是,我不希望仅处理“长时间运行”记录的容器实例因仅处理“短运行记录”的容器而变慢。 If this does conceptually not make sense (question to you), there would be no need to have isolated threads/workers for different containers. 如果这在概念上没有意义(问题),就不需要为不同的容器提供隔离的线程/工作者。

  2. Is implementing my own ExecutorService the proper way to go? 正在实施我自己的ExecutorService正确的方法吗? Can someone point me to a project that has a similar problem/solution? 有人能指出我有一个类似问题/解决方案的项目吗? Otherwise I guess I'd have to implement my own ExecutorService and share that amongst my Containers, or is there a better solution? 否则我想我必须实现自己的ExecutorService并在我的容器中共享,或者是否有更好的解决方案?

  3. Currently, I'm collecting all futures in the close method, since the container has to store the records/entries in the order they arrived. 目前,我正在以close方法收集所有期货,因为容器必须按照它们到达的顺序存储记录/条目。 Clearly, this requires a lot of time and therefore I'd like to do this once the Future is finished. 显然,这需要很多时间,因此一旦未来完成,我想做到这一点。 Does it make sense to have an additional worker solely processing the futures, or is it better to have my worker do that work, ie, having thread interaction. 有一个额外的工人单独处理未来是否有意义,或者让我的工人做这项工作更好,即有线程互动。

I do not think Java runtime provides something which meets your needs out of the box. 我不认为Java运行时提供了满足您开箱即用需求的东西。

I wrote my own class to serve such needs (common pool + restriction for given queue). 我编写了自己的类来满足这些需求(公共池+给定队列的限制)。

public static class ThreadLimitedQueue {
    private final ExecutorService executorService;
    private final int limit;

    private final Object lock = new Object();
    private final LinkedList<Runnable> pending = new LinkedList<>();
    private int inProgress = 0;

    public ThreadLimitedQueue(final ExecutorService executorService, final int limit) {
        this.executorService = executorService;
        this.limit = limit;
    }

    public void submit(Runnable runnable) {
        final Runnable wrapped = () -> {
            try {
                runnable.run();
            } finally {
                onComplete();
            }
        };
        synchronized (lock) {
            if (inProgress < limit) {
                inProgress++;
                executorService.submit(wrapped);
            } else {
                pending.add(wrapped);
            }
        }
    }

    private void onComplete() {
        synchronized (lock) {
            final Runnable pending = this.pending.poll();
            if (pending == null || inProgress > limit) {
                inProgress--;
            } else {
                executorService.submit(pending);
            }
        }
    }
}

The only difference in my case limit is constant but you can modify it. 我的案例limit的唯一区别是不变,但你可以修改它。 For example, you can replace int limit with Supplier<Integer> limitFunction and this function must provide dynamic limit, eg 例如,您可以使用Supplier<Integer> limitFunction替换int limit ,并且此函数必须提供动态限制,例如

Supplier<Integer> limitFunction = 12 / containersList.size();

Just make it more robust (eg what to do if containersList is empty or exceed 12) 只是让它更健壮(例如,如果containersList为空或超过12,该怎么办)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM