0

I have written a simple program, that is intended to start a few threads. The threads should then pick a integer n from an integer array, use it to wait n and return the time t the thread waited back into an array for the results.

If one thread finishes it's task, it should pick the next one, that has not yet being assigned to another thread.
Of course: The order in the arrays has to be maintained, so that integers and results match.

My code runs smoothly as far I see. However I use one line of code block I find in particular unsatisfying and hope there is a good way to fix this without changing too much:

while(Thread.activeCount() != 1); // first evil line

I kinda abuse this line to make sure all my threads finish getting all the tasks done, before I access my array with the results. I want to do that to prevent ill values, like 0.0, Null Pointer Exception... etc. (in short anything that would make an application with an actual use crash) Any sort of constructive help is appreciated. I am also not sure, if my code still runs smoothly for very very long arrays of tasks for the threads, for example the results no longer match the order of the integer.

Any constructive help is appreciated.

First class:

public class ThreadArrayWriterTest {

    int[] repitions;
    int len = 0;
    double[] timeConsumed;

    public boolean finished() {
        synchronized (repitions) {
            return len <= 0;
        }
    }

    public ThreadArrayWriterTest(int[] repitions) {
        this.repitions = repitions;
        this.len = repitions.length;
        timeConsumed = new double[this.len];
    }

    public double[] returnTimes(int[] repititions, int numOfThreads, TimeConsumer timeConsumer) {

        for (int i = 0; i < numOfThreads; i++) {
            new Thread() {
                public void run() {
                    while (!finished()) {
                        len--;
                        timeConsumed[len] = timeConsumer.returnTimeConsumed(repititions[len]);
                    }
                }

            }.start();
        }
        while (Thread.activeCount() != 1) // first evil line
            ;
        return timeConsumed;
    }

    public static void main(String[] args) {
        long begin = System.currentTimeMillis();
        int[] repitions = { 3, 1, 3, 1, 2, 1, 3, 3, 3 };
        int numberOfThreads = 10;

        ThreadArrayWriterTest t = new ThreadArrayWriterTest(repitions);
        double[] times = t.returnTimes(repitions, numberOfThreads, new TimeConsumer());
        for (double d : times) {
            System.out.println(d);
        }
        long end = System.currentTimeMillis();
        System.out.println("Total time of execution: " + (end - begin));
    }
}

Second class:

public class TimeConsumer {

    double returnTimeConsumed(int repitions) {
        long before = System.currentTimeMillis();
        for (int i = 0; i < repitions; i++) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        long after = System.currentTimeMillis();
        double ret = after - before;
        System.out.println("It takes: " + ret + "ms" + " for " + repitions + " runs through the for-loop");
        return ret;
    }
}
blauerschluessel
  • 219
  • 1
  • 3
  • 14

3 Answers3

1

The easiest way to wait for all threads to complete is to keep a Collection of them and then call Thread.join() on each one in turn.

sisyphus
  • 6,174
  • 1
  • 25
  • 35
1

In addition to .join() you can use ExecutorService to manage pools of threads,

An Executor that provides methods to manage termination and methods that can produce a Future for tracking progress of one or more asynchronous tasks.

An ExecutorService can be shut down, which will cause it to reject new tasks. Two different methods are provided for shutting down an ExecutorService. The shutdown() method will allow previously submitted tasks to execute before terminating, while the shutdownNow() method prevents waiting tasks from starting and attempts to stop currently executing tasks. Upon termination, an executor has no tasks actively executing, no tasks awaiting execution, and no new tasks can be submitted. An unused ExecutorService should be shut down to allow reclamation of its resources.

Method submit extends base method Executor.execute(Runnable) by creating and returning a Future that can be used to cancel execution and/or wait for completion. Methods invokeAny and invokeAll perform the most commonly useful forms of bulk execution, executing a collection of tasks and then waiting for at least one, or all, to complete.

ExecutorService executorService = Executors.newFixedThreadPool(maximumNumberOfThreads);
CompletionService completionService = new ExecutorCompletionService(executorService);
for (int i = 0; i < numberOfTasks; ++i) {
     completionService.take(); 
}
executorService.shutdown();

Plus take a look at ThreadPoolExecutor

Ravindra babu
  • 37,698
  • 11
  • 250
  • 211
Ahmad Sanie
  • 3,678
  • 2
  • 21
  • 56
0

Since java provides more advanced threading API with concurrent package, You should have look into ExecutorService, which simplifies thread management mechanism.

Simple to solution to your problem.

  1. Use Executors API to create thread pool

    static ExecutorService  newFixedThreadPool(int nThreads)
    

    Creates a thread pool that reuses a fixed number of threads operating off a shared unbounded queue.

  2. Use invokeAll to wait for all tasks to complete.

    Sample code:

    ExecutorService service = Executors.newFixedThreadPool(10);
    
    List<MyCallable> futureList = new ArrayList<MyCallable>();
    for ( int i=0; i<12; i++){
        MyCallable myCallable = new MyCallable((long)i);
        futureList.add(myCallable);
    }
    System.out.println("Start");
    try{
        List<Future<Long>> futures = service.invokeAll(futureList);  
        for(Future<Long> future : futures){
            try{
                System.out.println("future.isDone = " + future.isDone());
                System.out.println("future: call ="+future.get());
            }
            catch(Exception err1){
                err1.printStackTrace();
            }
        }
    }catch(Exception err){
        err.printStackTrace();
    }
    service.shutdown();
    

Refer to this related SE question for more details on achieving the same:

wait until all threads finish their work in java

Community
  • 1
  • 1
Ravindra babu
  • 37,698
  • 11
  • 250
  • 211