15

I need to process elements in some Collection instance concurrently. In other words instead of iterating a Collection instance

for (Someclass elem : coll){
     process(elem);
}

I’d like to process those elements concurrently. Say, something like ConcurrentCollectionExecutor(coll, new Callable{…}, numberOfThreads). Also, a number of simultaneous threads should be fixed.

Any flexible pattern already exists?

mipe34
  • 5,596
  • 3
  • 26
  • 38
serg kunz
  • 505
  • 1
  • 4
  • 9

4 Answers4

8

Make the process method a run() method in a class called MyRunnable that implements Runnable and whose constructor takes elem as input and stores it as an instance variable. Then use:

ExecutorService executor = Executors.newFixedThreadPool(numberOfThreads);
for (Someclass elem : coll){
   Runnable worker = new MyRunnable(elem);
   executor.execute(worker);
}
stepanian
  • 11,373
  • 8
  • 43
  • 63
8

A good solution would be:

  1. instantiate an ArrayBlockingQueue containing the elements to process
  2. instantiate an ExecutorService to execute your processing concurrently
  3. instantiate your Runnables giving them the ArrayBlockingQueue as parameter
  4. Implement the run method: while there elements in the queue, poll them and process them
  5. Submit your Runnables to the ExecutorService

The code:

BlockingQueue<Someclass> toProcess = 
    new ArrayBlockingQueue<Someclass>(coll.size(), false, coll);
ExecutorService es = Executors.newFixedThreadPool(numberOfThreads);
for(int count = 0 ; count < numberOfThreads ; ++c) {
    es.submit(new MyRunnable(toProcess));
}


private static class MyRunnable() implements Runnable {
    private final BlockingQueue<Someclass> toProcess;

    public MyRunnable(BlockingQueue<Someclass> toProcess) {
        this.toProcess = toProcess;
    }

    @Override
    public void run() {
        Someclass element = null;
        while((element = toProcess.poll()) != null) {
            process(element);
        }
    }
}
Jean Logeart
  • 52,687
  • 11
  • 83
  • 118
  • I think this is equivalent to stepanians answer. It just uses the executorservice as the queue of tasks instead of creating an explicit one. Yours is better if elements need be added to the queue. – brain Feb 08 '13 at 08:48
  • Calling process() directly would not have a great performance difference compared to the soon-to-be-inlined process() inside a run(). When there are enough cores or, worse, sockets, the scaling is limited by the queue implementation. So you want to make sure each queue poll or take is worth about 100 or more µs work. Vakimshaars example is only better if all jobs are there before starting the threads. Like is the case here. – Ralf H Feb 08 '13 at 12:30
2

Below the "hand-made" version of such executor class. Take a notice, you have to pass there not an instance of Callable (or Runnable) but class-name of such processor-class.

public class ConcurrentCollectionExecutor<T> {

private Collection<T> collection;
private Class<Runnable> processor;
private int numberOfThreads;
private Executor executor;

public ConcurrentCollectionExecutor(Collection<T> collection, Class<Runnable> processor, int numberOfThreads) {
    this.collection = collection;
    this.processor = processor;
    this.numberOfThreads = numberOfThreads;
    this.executor = Executors.newFixedThreadPool(numberOfThreads);
}

public void run() {
    try {
        Constructor<Runnable> constructor = null;
        for (T t : collection) {
            if (constructor == null) {
                constructor = processor.getConstructor(t.getClass());
            }
            executor.execute(constructor.newInstance(t));
        }
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}      
}
Andremoniy
  • 34,031
  • 20
  • 135
  • 241
  • 1
    ey @Andremoniy, I studied your code and I found it very interesting... however i would like to add some things to polish it a Little bit.... if you allow me, please... 1) The runnable implementation must has a constructor that takes as sole argument an element of the same type as the component type of the array. 2) You should add and the end of the run an `executor.shutdow()` 3) the instance field numberOfThreads is not being used. Hope to contribute and greetings! – Victor Aug 27 '15 at 02:29
0

I dont know any patterns for this, but as an Idea you can devide your collection elements on the number of threads, so each thread gets X Elements to process, for example:

Collection has 20 elements, youc all your function providing 4 Threads then intern you start them like:

thread1 gets the elements [0 .. 4]
thread2 gets the elements [5 .. 9]
thread3 gets the elements [10 .. 14]
thread1 gets the elements [15 .. 19]

Notice that deleting elements from the collection could cause problems then specially thread 4 tries to access element[19] while there are less than 20 elements in your collection.

EDIT:

As brain mentioned depending on the elements process time, this Idea it can be not effecient as if processing one of the first 5 elements took 10 seconds but the other elements only took 0.5 seconds then thread1 would be busy but the other threads would end up not running in parallel for very long.

CloudyMarble
  • 36,908
  • 70
  • 97
  • 130
  • I think this is weaker than the other two solutions because processing different elements could take different lengths of time. E.g. if processing one of the first 5 elements took 10 seconds but the other elements only took 0.5 seconds then thread1 would be busy but the other threads would end up not running in parallel for very long. – brain Feb 08 '13 at 08:45
  • @brain this is a very good point, i will write it in the answer, thank you. – CloudyMarble Feb 08 '13 at 08:48