简体   繁体   English

顺序和并行处理

[英]Both sequential and parallel processing

I have one producer and many consumers.我有一个生产者和许多消费者。

  • the producer is fast and generating a lot of results生产者速度快,结果很多
  • tokens with the same value need to be processed sequentially具有相同值的令牌需要依次处理
  • tokens with different values must be processed in parallel具有不同值的令牌必须并行处理
  • creating new Runnables would be very expensive and also the production code could work with 100k of Tokens(in order to create a Runnable I have to pass to the constructor some complex to build objects)创建新的 Runnable 将非常昂贵,而且生产代码可以使用 100k 令牌(为了创建一个 Runnable,我必须将一些复杂的构建对象传递给构造函数)

Can I achieve the same results with a simpler algorithm?我可以使用更简单的算法获得相同的结果吗? Nesting a syncronization block with a reentrant lock seems a bit unnatural.用可重入锁嵌套同步块似乎有点不自然。 Are there any race conditions you might notice?是否有任何您可能会注意到的竞争条件?

Update: a second solution I found was working with 3 collections.更新:我发现的第二个解决方案是使用 3 个集合。 One to cache the producer results, second a blocking queue and 3rd using a list to track in the tasks in progress.第一个缓存生产者结果,第二个阻塞队列,第三个使用列表跟踪正在进行的任务。 Again a bit to complicated.再次有点复杂。

My version of code我的代码版本

import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.locks.ReentrantLock;

public class Main1 {
    static class Token {
        private int order;
        private String value;
        Token() {

        }
        Token(int o, String v) {
            order = o;
            value = v;
        }

        int getOrder() {
            return order;
        }

        String getValue() {
            return value;
        }
    }

    private final static BlockingQueue<Token> queue = new ArrayBlockingQueue<Token>(10);
    private final static ConcurrentMap<String, Object> locks = new ConcurrentHashMap<String, Object>();
    private final static ReentrantLock reentrantLock = new ReentrantLock();
    private final static Token STOP_TOKEN = new Token();
    private final static List<String> lockList = Collections.synchronizedList(new ArrayList<String>());

    public static void main(String[] args) {
        ExecutorService producerExecutor = Executors.newSingleThreadExecutor();
        producerExecutor.submit(new Runnable() {
            public void run() {
                Random random = new Random();
                    try {
                        for (int i = 1; i <= 100; i++) {
                            Token token = new Token(i, String.valueOf(random.nextInt(1)));

                            queue.put(token);
                        }

                        queue.put(STOP_TOKEN);
                    }catch(InterruptedException e){
                        e.printStackTrace();
                    }
                }
        });

        ExecutorService consumerExecutor = Executors.newFixedThreadPool(10);
        for(int i=1; i<=10;i++) {

            // creating to many runnable would be inefficient because of this complex not thread safe object
            final Object dependecy = new Object(); //new ComplexDependecy()
            consumerExecutor.submit(new Runnable() {
                public void run() {
                    while(true) {
                        try {
                            //not in order


                            Token token = queue.take();
                            if (token == STOP_TOKEN) {
                                queue.add(STOP_TOKEN);
                                return;
                            }


                            System.out.println("Task start" + Thread.currentThread().getId() + " order "  + token.getOrder());

                            Random random = new Random();
                            Thread.sleep(random.nextInt(200)); //doLongRunningTask(dependecy)
                            lockList.remove(token.getValue());

                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
            }});

    }
}}

You can pre-create set of Runnables which will pick incoming tasks (tokens) and place them in queues according to their order value.您可以预先创建一组Runnables ,它们将选择传入的任务(令牌)并根据它们的顺序值将它们放入队列中。

As pointed out in comments, it's not guaranteed that tokens with different values will always execute in parallel (all in all, you are bounded, at least, by nr of physical cores in your box).正如评论中指出的那样,不能保证具有不同值的令牌总是并行执行(总而言之,您至少受到盒子中物理内核数的限制)。 However, it is guaranteed that tokens with same order will be executed in the order of arrival.但是,保证相同顺序的代币将按照到达的顺序执行。

Sample code:示例代码:

/**
 * Executor which ensures incoming tasks are executed in queues according to provided key (see {@link Task#getOrder()}).
 */
public class TasksOrderingExecutor {

    public interface Task extends Runnable {
        /**
         * @return ordering value which will be used to sequence tasks with the same value.<br>
         * Tasks with different ordering values <i>may</i> be executed in parallel, but not guaranteed to.
         */
        String getOrder();
    }

    private static class Worker implements Runnable {

        private final LinkedBlockingQueue<Task> tasks = new LinkedBlockingQueue<>();

        private volatile boolean stopped;

        void schedule(Task task) {
            tasks.add(task);
        }

        void stop() {
            stopped = true;
        }

        @Override
        public void run() {
            while (!stopped) {
                try {
                    Task task = tasks.take();
                    task.run();
                } catch (InterruptedException ie) {
                    // perhaps, handle somehow
                }
            }
        }
    }

    private final Worker[] workers;
    private final ExecutorService executorService;

    /**
     * @param queuesNr nr of concurrent task queues
     */
    public TasksOrderingExecutor(int queuesNr) {
        Preconditions.checkArgument(queuesNr >= 1, "queuesNr >= 1");
        executorService = new ThreadPoolExecutor(queuesNr, queuesNr, 0, TimeUnit.SECONDS, new SynchronousQueue<>());
        workers = new Worker[queuesNr];
        for (int i = 0; i < queuesNr; i++) {
            Worker worker = new Worker();
            executorService.submit(worker);
            workers[i] = worker;
        }
    }

    public void submit(Task task) {
        Worker worker = getWorker(task);
        worker.schedule(task);
    }

    public void stop() {
        for (Worker w : workers) w.stop();
        executorService.shutdown();
    }

    private Worker getWorker(Task task) {
        return workers[task.getOrder().hashCode() % workers.length];
    }
}

By the nature of your code, the only way to guarantee that the tokens with the same value are processed in serial manner is to wait for STOP_TOKEN to arrive.根据您的代码的性质,保证具有相同值的令牌以串行方式处理的唯一方法是等待 STOP_TOKEN 到达。

You'll need single producer-single consumer setup, with consumer collecting and sorting the tokens by their value (into the Multimap, let say).您将需要单生产者-单消费者设置,消费者按其值收集和排序令牌(例如,进入 Multimap)。

Only then you know which tokens can be process serially and which may be processed in parallel.只有这样您才能知道哪些令牌可以串行处理,哪些可以并行处理。

Anyway, I advise you to look at LMAX Disruptor , which offers very effective way for sharing data between threads.无论如何,我建议您查看LMAX Disruptor ,它提供了非常有效的线程间数据共享方式。

It doesn't suffer from synchronization overhead as Executors as it is lock free (which may give you nice performance benefits, depending on the way how you process the data).它不会像 Executors 那样受到同步开销的影响,因为它是无锁的(这可能会给您带来不错的性能优势,具体取决于您处理数据的方式)。

The solution using two Disruptors使用两个 Disruptor 的解决方案

// single thread for processing as there will be only on consumer
Disruptor<InEvent> inboundDisruptor = new Disruptor<>(InEvent::new, 32, Executors.newSingleThreadExecutor());

// outbound disruptor that uses 3 threads for event processing
Disruptor<OutEvent> outboundDisruptor = new Disruptor<>(OutEvent::new, 32, Executors.newFixedThreadPool(3));

inboundDisruptor.handleEventsWith(new InEventHandler(outboundDisruptor));

// setup 3 event handlers, doing round robin consuming, effectively processing OutEvents in 3 threads
outboundDisruptor.handleEventsWith(new OutEventHandler(0, 3, new Object()));
outboundDisruptor.handleEventsWith(new OutEventHandler(1, 3, new Object()));
outboundDisruptor.handleEventsWith(new OutEventHandler(2, 3, new Object()));

inboundDisruptor.start();
outboundDisruptor.start();

// publisher code
for (int i = 0; i < 10; i++) {
    inboundDisruptor.publishEvent(InEventTranslator.INSTANCE, new Token());
}

The event handler on the inbound disruptor just collects incoming tokens.入站干扰器上的事件处理程序只收集传入的令牌。 When STOP token is received, it publishes the series of tokens to outbound disruptor for further processing:当收到 STOP 令牌时,它将一系列令牌发布到出站干扰器以进行进一步处理:

public class InEventHandler implements EventHandler<InEvent> {

    private ListMultimap<String, Token> tokensByValue = ArrayListMultimap.create();
    private Disruptor<OutEvent> outboundDisruptor;

    public InEventHandler(Disruptor<OutEvent> outboundDisruptor) {
        this.outboundDisruptor = outboundDisruptor;
    }

    @Override
    public void onEvent(InEvent event, long sequence, boolean endOfBatch) throws Exception {
        if (event.token == STOP_TOKEN) {
            // publish indexed tokens to outbound disruptor for parallel processing
            tokensByValue.asMap().entrySet().stream().forEach(entry -> outboundDisruptor.publishEvent(OutEventTranslator.INSTANCE, entry.getValue()));
        } else {
            tokensByValue.put(event.token.value, event.token);
        }
    }
}

Outbound event handler processes tokens of the same value sequentially:出站事件处理程序按顺序处理相同值的令牌:

public class OutEventHandler implements EventHandler<OutEvent> {

    private final long order;
    private final long allHandlersCount;
    private Object yourComplexDependency;

    public OutEventHandler(long order, long allHandlersCount, Object yourComplexDependency) {
        this.order = order;
        this.allHandlersCount = allHandlersCount;
        this.yourComplexDependency = yourComplexDependency;
    }

    @Override
    public void onEvent(OutEvent event, long sequence, boolean endOfBatch) throws Exception {
        if (sequence % allHandlersCount != order ) {
            // round robin, do not consume every event to allow parallel processing
            return;
        }

        for (Token token : event.tokensToProcessSerially) {
            // do procesing of the token using your complex class
        }

    }
}

The rest of the required infrastructure (purpose described in the Disruptor docs):其余所需的基础设施(Disruptor 文档中描述的目的):

public class InEventTranslator implements EventTranslatorOneArg<InEvent, Token> {

    public static final InEventTranslator INSTANCE = new InEventTranslator();

    @Override
    public void translateTo(InEvent event, long sequence, Token arg0) {
        event.token = arg0;
    }

}

public class OutEventTranslator implements EventTranslatorOneArg<OutEvent, Collection<Token>> {

    public static final OutEventTranslator INSTANCE = new OutEventTranslator();

    @Override
    public void translateTo(OutEvent event, long sequence, Collection<Token> tokens) {
        event.tokensToProcessSerially = tokens;
    }
}


public class InEvent {

    // Note that no synchronization is used here,
    // even though the field is used among multiple threads.
    // Memory barrier used by Disruptor guarantee changes are visible.
    public Token token;
}

public class OutEvent {
    // ... again, no locks.
    public Collection<Token> tokensToProcessSerially;

}

public class Token {
    String value;

}

If you have lots of different tokens, then the simplest solution is to create some number of single-thread executors (about 2x your number of cores), and then distribute each task to an executor determined by the hash of its token.如果您有很多不同的令牌,那么最简单的解决方案是创建一定数量的单线程执行程序(大约是内核数量的 2 倍),然后将每个任务分配给由其令牌哈希确定的执行程序。

That way all tasks with the same token will go to the same executor and execute sequentially, because each executor only has one thread.这样所有具有相同令牌的任务都会转到同一个执行器并按顺序执行,因为每个执行器只有一个线程。

If you have some unstated requirements about scheduling fairness, then it is easy enough to avoid any significant imbalances by having the producer thread queue up its requests (or block) before distributing them, until there are, say, less than 10 requests per executor outstanding.如果您对调度公平性有一些未说明的要求,那么通过让生产者线程在分发请求(或块)之前将其请求(或块)排队,直到每个执行器未完成的请求少于 10 个,就很容易避免任何严重的不平衡.

The following solution will only use a single Map that is used by the producer and consumers to process orders in sequential order for each order number while processing different order numbers in parallel.以下解决方案将仅使用生产者和消费者使用的单个 Map 来按顺序处理每个订单号的订单,同时并行处理不同的订单号。 Here is the code:这是代码:

public class Main {

    private static final int NUMBER_OF_CONSUMER_THREADS = 10;
    private static volatile int sync = 0;

    public static void main(String[] args) {
        final ConcurrentHashMap<String,Controller> queues = new ConcurrentHashMap<String, Controller>();
        final CountDownLatch latch = new CountDownLatch(NUMBER_OF_CONSUMER_THREADS);
        final AtomicBoolean done = new AtomicBoolean(false);

        // Create a Producer
        new Thread() {
            {
                this.setDaemon(true);
                this.setName("Producer");
                this.start();
            }

            public void run() {
                Random rand = new Random();

                for(int i =0 ; i < 1000 ; i++) {
                    int order = rand.nextInt(20);
                    String key = String.valueOf(order);
                    String value = String.valueOf(rand.nextInt());
                    Controller controller = queues.get(key);
                    if (controller == null) {
                        controller = new Controller();
                        queues.put(key, controller);
                    }
                    controller.add(new Token(order, value));
                    Main.sync++;
                }

                done.set(true);
            }
        };

        while (queues.size() < 10) {
            try {
                // Allow the producer to generate several entries that need to
                // be processed.
                Thread.sleep(5000);
            } catch (InterruptedException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            }
        }

        // System.out.println(queues);

        // Create the Consumers
        ExecutorService consumers = Executors.newFixedThreadPool(NUMBER_OF_CONSUMER_THREADS);
        for(int i = 0 ; i < NUMBER_OF_CONSUMER_THREADS ; i++) {
            consumers.submit(new Runnable() {
                private Random rand = new Random();

                public void run() {
                    String name = Thread.currentThread().getName();
                    try {
                        boolean one_last_time = false;
                        while (true) {
                            for (Map.Entry<String, Controller> entry : queues.entrySet()) {
                                Controller controller = entry.getValue();
                                if (controller.lock(this)) {
                                    ConcurrentLinkedQueue<Token> list = controller.getList();
                                    Token token;
                                    while ((token = list.poll()) != null) {
                                        try {
                                            System.out.println(name + " processing order: " + token.getOrder()
                                                    + " value: " + token.getValue());
                                            Thread.sleep(rand.nextInt(200));
                                        } catch (InterruptedException e) {
                                        }
                                    }
                                    int last = Main.sync;
                                    queues.remove(entry.getKey());
                                    while(done.get() == false && last == Main.sync) {
                                        // yield until the producer has added at least another entry
                                        Thread.yield();
                                    }
                                    // Purge any new entries added
                                    while ((token = list.poll()) != null) {
                                        try {
                                            System.out.println(name + " processing order: " + token.getOrder()
                                                    + " value: " + token.getValue());
                                            Thread.sleep(200);
                                        } catch (InterruptedException e) {
                                        }
                                    }
                                    controller.unlock(this);
                                }
                            }
                            if (one_last_time) {
                                return;
                            }
                            if (done.get()) {
                                one_last_time = true;
                            }
                        }
                    } finally {
                        latch.countDown();
                    }
                }
            });
        }
        try {
            latch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        consumers.shutdown();
        System.out.println("Exiting.. remaining number of entries: " + queues.size());
    }

}

Note that the Main class contains a queues instance that is a Map.请注意, Main 类包含一个队列实例,它是一个 Map。 The map key is the order id that you want to process sequentially by the consumers.映射键是您希望消费者按顺序处理的订单 ID。 The value is a Controller class that will contain all of the orders associated with that order id.该值是一个 Controller 类,它将包含与该订单 ID 关联的所有订单。

The producer will generate the orders and add the order, (Token), to its associated Controller.生产者将生成订单并将订单(令牌)添加到其关联的控制器。 The consumers will iterator over the queues map values and call the Controller lock method to determine if it can process orders for that particular order id.消费者将遍历队列映射值并调用控制器锁定方法以确定它是否可以处理该特定订单 ID 的订单。 If the lock returns false it will check the next Controller instance.如果锁返回 false,它将检查下一个 Controller 实例。 If the lock returns true, it will process all orders and then check the next Controller.如果锁返回true,它将处理所有订单,然后检查下一个Controller。

updated Added the sync integer that is used to guarantee that when an instance of the Controller is removed from the queues map.更新添加了同步整数,用于保证当控制器的实例从队列映射中删除时。 All of its entries will be consumed.它的所有条目都将被消耗。 There was an logic error in the consumer code where the unlock method was called to soon.消费者代码中存在逻辑错误,即将调用解锁方法。

The Token class is similar to the one that you've posted here. Token 类与您在此处发布的类相似。

class Token {
    private int order;
    private String value;

    Token(int order, String value) {
        this.order = order;
        this.value = value;
    }

    int getOrder() {
        return order;
    }

    String getValue() {
        return value;
    }

    @Override
    public String toString() {
        return "Token [order=" + order + ", value=" + value + "]\n";
    }
}

The Controller class that follows is used to insure that only a single thread within the thread pool will be processing the orders.后面的 Controller 类用于确保线程池中只有一个线程将处理订单。 The lock/unlock methods are used to determine which of the threads will be allowed to process the orders.锁定/解锁方法用于确定允许哪个线程处理订单。

class Controller {

    private ConcurrentLinkedQueue<Token> tokens = new ConcurrentLinkedQueue<Token>();
    private ReentrantLock lock = new ReentrantLock();
    private Runnable current = null;

    void add(Token token) {
        tokens.add(token);
    }

    public ConcurrentLinkedQueue<Token> getList() {
        return tokens;
    }

    public void unlock(Runnable runnable) {
        lock.lock();
        try {
            if (current == runnable) {
                current = null;
            }
        } finally {
            lock.unlock();
        }
    }

    public boolean lock(Runnable runnable) {
        lock.lock();
        try {
            if (current == null) {
                current = runnable;
            }
        } finally {
            lock.unlock();
        }
        return current == runnable;
    }

    @Override
    public String toString() {
        return "Controller [tokens=" + tokens + "]";
    }

}

Additional information about the implementation.有关实施的其他信息。 It uses a CountDownLatch to insure that all produced orders will be processed prior to the process exiting.它使用 CountDownLatch 来确保所有生成的订单将在流程退出之前得到处理。 The done variable is just like your STOP_TOKEN variable. done 变量就像你的 STOP_TOKEN 变量。

The implementation does contain an issue that you would need to resolve.该实现确实包含您需要解决的问题。 There is the issue that it does not purge the controller for an order id when all of the orders have been processed.存在的问题是,在处理完所有订单后,它不会清除控制器中的订单 ID。 This will cause instances where a thread in the thread pool gets assigned to a controller that contains no orders.这将导致线程池中的线程被分配给不包含订单的控制器的实例。 Which will waste cpu cycles that could be used to perform other tasks.这将浪费可用于执行其他任务的 CPU 周期。

Is all you need is to ensure that tokens with the same value are not being processed concurrently?您只需要确保不会同时处理具有相同值的令牌吗? Your code is too messy to understand what you mean (it does not compile, and has lots of unused variables, locks and maps, that are created but never used).你的代码太乱了,无法理解你的意思(它不能编译,并且有很多未使用的变量、锁和映射,它们被创建但从未使用过)。 It looks like you are greatly overthinking this.看起来你想得太多了。 All you need is one queue, and one map.您只需要一个队列和一张地图。 Something like this I imagine:我想象的事情是这样的:

   class Consumer implements Runnable {
     ConcurrentHashMap<String, Token> inProcess;
     BlockingQueue<Token> queue;

     public void run() {
        Token token = null;
        while ((token = queue.take()) != null) {
           if(inProcess.putIfAbsent(token.getValue(), token) != null) {
              queue.put(token);
              continue;
           }
           processToken(token);
           inProcess.remove(token.getValue());
        }
     }
   }

tokens with the same value need to be processed sequentially具有相同值的令牌需要依次处理

The way to insure that any two things happen in sequence is to do them in the same thread.确保任何两件事按顺序发生的方法是在同一个线程中完成它们。

I'd have a collection of however many worker threads, and I'd have a Map.我有一个包含许多工作线程的集合,我有一个 Map。 Any time I get a token that I've not seen before, I'll pick a thread at random, and enter the token and the thread into the map.每当我得到一个我以前从未见过的令牌时,我都会随机选择一个线程,并将令牌和线程输入到地图中。 From then on, I'll use that same thread to execute tasks associated with that token.从那时起,我将使用同一个线程来执行与该令牌关联的任务。

creating new Runnables would be very expensive创建新的 Runnable 将非常昂贵

Runnable is an interface. Runnable是一个接口。 Creating new objects that implement Runnable is not going to be significantly more expensive than creating any other kind of object.创建实现Runnable新对象不会比创建任何其他类型的对象昂贵得多。

Maybe I'm misunderstanding something.也许我误解了一些东西。 But it seems that it would be easier to filter the Tokens with same value from the ones with different values into two different queues initially.但似乎最初将具有相同值的令牌从具有不同值的令牌过滤到两个不同的队列中会更容易。

And then use Stream with either map or foreach for the sequential.然后将 Stream 与 map 或 foreach 一起用于顺序。 And simply use the parallel stream version for the rest.其余的只需使用并行流版本。

If your Tokens in production environment are lazily generated and you only get one at a time you simply make some sort of filter which distributes them to the two different queues.如果您的生产环境中的 Token 是懒惰生成的,并且一次只能获得一个,您只需制作某种过滤器,将它们分发到两个不同的队列。

If you can implement it with Streams I suqqest doing that as they are simple, easy to use and FAST!如果您可以使用 Streams 来实现它,我最愿意这样做,因为它们简单、易于使用且速度快!

https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html

I made a brief example of what I mean.我举了一个简短的例子来说明我的意思。 In this case the numbers Tokens are sort of artificially constructed but thats beside the point.在这种情况下,数字令牌是人工构建的,但这不是重点。 Also the streams are both initiated on the main thread which would probably also not be ideal.此外,流都在主线程上启动,这可能也不理想。

public static void main(String args[]) {
    ArrayList<Token> sameValues = new ArrayList<Token>();
    ArrayList<Token> distinctValues = new ArrayList<Token>();
    Random random = new Random();
    for (int i = 0; i < 100; i++) {
        int next = random.nextInt(100);
        Token n = new Token(i, String.valueOf(next));
        if (next == i) {
            sameValues.add(n);
        } else {
            distinctValues.add(n);
        }
    }       
    distinctValues.stream().parallel().forEach(token -> System.out.println("Distinct: " + token.value));
    sameValues.stream().forEach(token -> System.out.println("Same: " + token.value));       
}

I am not entirely sure I have understood the question but I'll take a stab at an algorithm.我不完全确定我是否理解了这个问题,但我会尝试一下算法。

The actors are:演员是:

  • A queue of tasks任务queue
  • A pool of free executors自由executors pool
  • A set of in-process tokens currently being processed当前正在处理的一set in-process令牌
  • A controller controller

Then,然后,

  • Initially all executors are available and the set is empty最初所有executors都可用并且set为空

  • controller picks an available executor and goes through the queue looking for a task with a token that is not in the in-process set and when it finds it controller选择一个可用的executor ,并通过queue寻找一个带有不在in-process set中的令牌的task ,并在它找到它时

    • adds the token to the in-process set将令牌添加到in-process
    • assigns the executor to process the task and分配executor来处理task
    • goes back to the beginning of the queue回到队列的开头
  • the executor removes the token from the set when it is done processing and adds itself back to the pool executor在完成处理后从set删除令牌并将自己添加回池中

One way of doing this is having one executor for sequence processing and one for parallel processing.这样做的一种方法是让一个执行器用于序列处理,另一个用于并行处理。 We also need a single threaded manager service that will decide to which service token needs to be submitted for processing.我们还需要一个单线程管理器服务,它将决定需要提交哪个服务令牌进行处理。 // Queue to be shared by both the threads. // 两个线程共享的队列。 Contains the tokens produced by producer.包含生产者生产的代币。
BlockingQueue tokenList = new ArrayBlockingQueue(10); BlockingQueue tokenList = new ArrayBlockingQueue(10);

    private void startProcess() {
    ExecutorService producer = Executors.newSingleThreadExecutor();
    final ExecutorService consumerForSequence = Executors
            .newSingleThreadExecutor();
    final ExecutorService consumerForParallel = Executors.newFixedThreadPool(10);
    ExecutorService manager = Executors.newSingleThreadExecutor();

    producer.submit(new Producer(tokenList));

    manager.submit(new Runnable() {

        public void run() {
            try {
                while (true) {
                    Token t = tokenList.take();
                    System.out.println("consumed- " + t.orderid
                            + " element");

                    if (t.orderid % 7 == 0) { // any condition to check for sequence processing

                        consumerForSequence.submit(new ConsumerForSequenceProcess(t));

                    } else {

                        ConsumerForParallel.submit(new ConsumerForParallelProcess(t));

                    }
                }
            }

            catch (InterruptedException e) { // TODO Auto-generated catch
                // block
                e.printStackTrace();
            }

        }
    });
}

I think there is a more fundamental design issue hidden behind this task, but ok.我认为这个任务背后隐藏着一个更基本的设计问题,但没关系。 I cant figure out from you problem description if you want in-order execution or if you just want operations on tasks described by single tokens to be atomic/transactional.我无法从您的问题描述中弄清楚您是否想要按顺序执行,或者您是否只想对由单个令牌描述的任务进行的操作是原子/事务性的。 What i propose below feels more like a "quick fix" to this issue than a real solution.我在下面建议的感觉更像是对这个问题的“快速修复”,而不是真正的解决方案。

For the real "ordered execution" case I propose a solution which is based on queue proxies which order the output:对于真正的“有序执行”案例,我提出了一个基于队列代理的解决方案,该代理对输出进行排序:

  1. Define a implementation of Queue which provides a factory method generating proxy queues which are represented to the producer side by a this single queue object;定义 Queue 的实现,它提供了一个工厂方法来生成代理队列,这些代理队列由这个单个队列对象表示给生产者; the factory method should also register these proxy queue objects.工厂方法还应该注册这些代理队列对象。 adding an element to the input queue should add it directly to one of the output queues if it matches one of the elements in one of the output queues.如果将元素添加到输入队列,则应将其直接添加到输出队列之一,如果它与输出队列之一中的元素之一匹配。 Otherwise add it to any (the shortest) output queue.否则将其添加到任何(最短的)输出队列。 (implement the check for this efficiently). (有效地对此进行检查)。 Alternatively (slightly better): don't do this when the element is added, but when any of the output queues runs empty.或者(稍微好一点):不要在添加元素时执行此操作,而是在任何输出队列为空时执行此操作。

  2. Give each of your runnable consumers an field storing an individual Queue interface (instead of accessing a single object).为每个可运行的消费者提供一个存储单独 Queue 接口的字段(而不是访问单个对象)。 Initialize this field by a the factory method defined above.通过上面定义的工厂方法初始化这个字段。

For the transaction case i think it's easier to span more threads than you have cores (use statistics to calculate this), and implement the blocking mechanism on an lower (object) level.对于事务情况,我认为跨越更多线程比拥有核心更容易(使用统计数据来计算),并在较低(对象)级别实现阻塞机制。

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

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