Search code examples
javamultithreadingswingexecutorserviceswingworker

ExecutorService argument to SwingWorker


I have an abstract class extending SwingWorker called FetchWorker to do some background data fetching and modifying the data that is fetched for my application. In my FetchWorker is an inner class of StatisticLayerController. This StatisticLayerController is extended by two classes. I initialize new threads within my FetchWorker to do some calculations. I used to use the ExecutorService of TrackHistoryLayerController.

Like below:

public class TrackHistoryLayerController extends StatisticLayerController
{
   private final ExecutorService heatMapAdderExecutor;
   ...

   public AdsbFetchWorker(...)  extends FetchWorker
   {
      super(...);
   }

   @Override
   protected final List<ADSBTrack> doInBackground() throws Exception
   {
      filtered.forEach( track -> {
         this.heatMapAdderExecutor.submit( new HeatmapAdderHelper( ... ) );
      } );
      while ( this.latch.getCount() != 0 )
      {
         this.publishValue( ( int ) this.latch.getCount() );
      }
      this.latch.await();
      this.publishValue( ( int ) this.latch.getCount() );
      if ( this.createImage() )
      {
         this.placeImage();
      }
      return filtered;
   }
}

So in this case HeatMapAdderHelper is my Helper thread which does some calculation for me. It was all working and all was fine. But now I wanted to change class structure a bit more, I wanted to make my Controller class abstract and I no longer wanted my Worker classes to be an inner class.


My question is, I cannot(should not) create ExecutorService within a Worker since the worker is going to be initialized each time event is invoked. Only thing I could do would be passing the ExecutorService from the Controller as an argument to the SwingWorker but would that be a good practice? Thank you in advance.


Solution

  • You can of course also just use the default ExecutorService provided by CompletableFuture by doing

    filtered.forEach( track -> {
         CompletableFuture.runAsync(new HeatmapAdderHelper( ... ) );
      } );
    

    As a side note, this

      while ( this.latch.getCount() != 0 )
      {
         this.publishValue( ( int ) this.latch.getCount() );
      }
      this.latch.await();
      this.publishValue( ( int ) this.latch.getCount() );
    

    looks very dodgy... a busy loop while waiting for the results? Also, why do you await the latch after you already waited in the loop? Assuming the latch is a CountDownLatch.

    I'm sure if you provided even more context, we could give a better overall solution.

    It looks like you can just do something like

    CompletableFuture<Void>[] futures = 
         filtered.stream().map(t -> new HeatmapAdderHelper(t))
                     .map(CompletableFuture::runAsync)
                     .toArray(CompletableFuture[]::new);
    CompletableFuture.allOf(futures).andThen(createImage());
    

    Or with a CountDownLatch:

    CountDownLatch latch = new CountDownLatch(filtered.size());
    filtered.forEach(f -> {
        CompletableFuture.runAsync(new HeatmapAdderHelper(f))
                         .thenRun(latch::countDown);
    });
    latch.await();
    createImage();