Performing tasks concurrently
SwingWorker comes with all the control features you try to emulate here. It already uses an ExecutorService under the hood. I'm not sure why you want to increase the maximum pool size, but given that the code does not compile as given, makes me suspect you did not decide this, based on performance measuring.

So here's what I did :


Launching the workers is lightweight enough to not need
to be run in a separate thread, so I inlined Starter. 
Keeping tabs on task progression is done through the Future that you get from
an ExecutorService. SwingWorker exposes this by implementing Future
itself. Waiting until a SwingWorker is done is as simple as calling its
get() method, which will block until the task is done.
You don't need the low level constructs wait() and notifyAll(). 
Properly handle InterruptedException : put it in your throws clause, or if you catch
it, reset the Thread's interrupted flag by calling
Thread.currentThread().interrupt(); so clients of your code know the thread has been
interrupted, and they can do their own cleanup.
SwingWorker uses generics to know the type of the result. Typically when no result is
given, Void is chosen as a return type. (this makes a comment about not caring about
the return value superfluous).
I've spent attention to the visibility modifiers, tightening it wherever possible,
exposing only as much as is strictly needed.
Future.get() throws an ExecutionException if the underlying task execution
threw an exception. Your code had no means of dealing with those.


Here's my refactoring of your code :

public class ImproveMe {
    private final List&lt;SwingWorker&gt; workers;

    public ImproveMe(Task[] tasks) {
        workers = new ArrayList&lt;&gt;(tasks.length);
        for (Task task : tasks) {
            workers.add(new TaskWorker(task));
        }
    }

    /* User will call this to start the loading */
    public void startLoading() {
        for (SwingWorker worker : workers) {
            worker.execute();
        }
    }

    /* User will call this to ensure that everything has complete before continuing */
    public void blockUntilDone() throws InterruptedException {
        for (SwingWorker worker : workers) {
            waitForWorker(worker);
        }
    }

    private void waitForWorker(SwingWorker worker) throws InterruptedException {
        try {
            worker.get(); // will block until the worker's task is done
        } catch (ExecutionException e) {
            throw new RuntimeException(e); // more appropriate exception handling here
        }
    }

    private static class TaskWorker extends SwingWorker&lt;Void, Void&gt; {
        private final Task t;

        TaskWorker( Task t ) {
            this.t = t;
        }

        public Void doInBackground() {
            if ( t != null ) {
                //do stuff with 't'
            }
            return null;
        }
    }
}