0

At present I have been using a FixedThreadPool to subdivide a large task; this has all been working fine. However; I have now found that part of one of these tasks can itself be subdivided. I used tried to submit further Callables to the FixedThreadPool but the program hung on the Future#get() (marked in the code)

The following program replicates the problem (I have used a FixedThreadPool with size 1 to worsen the problem)

public class ThreadPoolTest {
    static ExecutorService threadPool=Executors.newFixedThreadPool(1);

    //method run by inner callable
    public void printText(){
        try {
            Thread.sleep(100);
        } catch (InterruptedException ex) {
            Logger.getLogger(ThreadPoolTest.class.getName()).log(Level.SEVERE, null, ex);
        }
        System.out.println("Printed from within thread");
    }

    //method run by outer callable
    public void testThreadPool(){

        Callable<Void> printOnAThread=()->{printText(); return null; };

        Future<Void> f2=threadPool.submit(printOnAThread);

        try {    
            System.out.println("Called");
            f2.get(); //<--hangs here
        } catch (InterruptedException | ExecutionException ex) {
            Logger.getLogger(ThreadPoolTest.class.getName()).log(Level.SEVERE, null, ex);
            throw new RuntimeException("Failed to print in thread", ex);
        }
    }


    public static void testThreadWithinThread(){
        ThreadPoolTest t=new ThreadPoolTest();

        Callable<Void> testCallable=()->{t.testThreadPool();return null;};

        Future<Void> f=threadPool.submit(
            testCallable
        );

        try {
            f.get();
        } catch (InterruptedException | ExecutionException ex) {
            Logger.getLogger(ThreadPoolTest.class.getName()).log(Level.SEVERE, null, ex);
            throw new RuntimeException("Main thread failed", ex);
        }
    }

    public static void main(String[] args){
        testThreadWithinThread();
        threadPool.shutdown();
        System.out.println("Program exits");
    }
}

What I expected to happen

  1. First thread is submitted. testThreadWithinThread() runs
  2. testThreadWithinThread() submits a Callable (()->{t.testThreadPool();return null;};)
  3. Submitted callable starts running "immediately"
  4. t.testThreadPool(); starts running
  5. testThreadPool(); itself submits an inner callable ()->{printText(); return null; };
  6. Inner callable cannot yet run because the thread pool isn't free
  7. f.get(); is reached, the outer callable blocks and waits. This releases the thread within the FixedThreadPool
  8. The inner callable now runs to completion
  9. f2.get(); is no longer blocked, outer callable runs to completion

What actually happened

Steps 1-6 happens as I expected however at point 7 when the outer callable is blocked; for some reason it doesn't release the thread and so the program hangs.

Question

Why does the program hang at this point? Is there any way I can submit callables from within callables safely?

Richard Tingle
  • 16,906
  • 5
  • 52
  • 77

1 Answers1

2

Your thread pool contains 1 thread. It can only execute one callable/runnable at a time. All other submitted tasks are queued until a thread is available to execute them.

Increase your pool size.

Sotirios Delimanolis
  • 274,122
  • 60
  • 696
  • 724
  • Yes, but why is that 1 thread not released when it reaches the `.get()`. Since it effectively stops doing anything at that point – Richard Tingle May 15 '14 at 21:45
  • @RichardTingle Why should it be released? The javadoc of `get()` states `Waits if necessary [...]`. This is a synchronous operation. – Sotirios Delimanolis May 15 '14 at 21:46
  • 1
    @RichardTingle Your first submitted tasks gets picked up by the only thread. It then submits a new task. That task gets queued up. and makes a `Future` available. The thread now calls `Future#get()`. That will sleep until the task is completed. But it will never be completed because there are no available threads to execute it. – Sotirios Delimanolis May 15 '14 at 21:48
  • So is it fair to say then that once a Callable starts it will not release its thread back to the pool until it has completed its task? Even if it has nothing to do? A quick test program suggests that is true. I placed a Callable that does nothing but `Thread.sleep(10000)` and that also holds up the queue till its finished (although arguable that *should* effect the thread itself anyway) – Richard Tingle May 15 '14 at 21:50
  • Now that I think about how the ThreadPool is probably implemented it feels obvious that it cannot leave a half completed callable; to do that the callable would have to be a thread in its own right, which would make the threadpool pointless. I could increase the threadpool size but I don't really want to since (in the real program) I've set it to be equal to the number of logical processors which seems sensible and would probably have to make it excessively large to garantee this can't occure. I may attempt to adjust the program such that callables don't submit more callables themselves. – Richard Tingle May 15 '14 at 22:10
  • @RichardTingle Sorry I was gone for a while. Think about it this way. The `Thread`s that the pool uses simply loop, retrieving from the queue of `Callable`/`Runnable` instances and executing those that it picks up synchronously. The `Thread` doesn't actually stop. – Sotirios Delimanolis May 15 '14 at 22:42
  • @Sotiros yes, it's when that occured to me that it all made sense. I think I was imagining the threads as if they were cores and the callables as if they were threads. Which is all clearly wrong – Richard Tingle May 15 '14 at 22:48