JDK-8145304 : Executors.newSingleThreadExecutor().submit(runnable) throws RejectedExecutionException
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.util.concurrent
  • Affected Version: 8u65,9
  • Priority: P4
  • Status: Open
  • Resolution: Unresolved
  • OS: generic
  • CPU: generic
  • Submitted: 2015-12-12
  • Updated: 2016-12-09
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other
tbd_majorUnresolved
Related Reports
Duplicate :  
Relates :  
Relates :  
Description
FULL PRODUCT VERSION :
java version "1.8.0_51"
Java(TM) SE Runtime Environment (build 1.8.0_51-b16)
Java HotSpot(TM) 64-Bit Server VM (build 25.51-b03, mixed mode) 

ADDITIONAL OS VERSION INFORMATION :
linux_suse_sles_11 and windows 7-64bit

A DESCRIPTION OF THE PROBLEM :
When spring framework init a bean object, it will invoke afterPropertiesSet() method.
My code like this:
public void afterPropertiesSet()
	{
		Executors.newSingleThreadExecutor().submit(new Runnable()
		{
			@Override
			public void run()
			{
				Thread.currentThread().setName("StartUp-1");
				try
				{
				   .....
				}
				catch (Exception e)
				{
					logger.error("", e);
				}
			}
			
		});
	}
but there throw a RejectedExeception:
Caused by: java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.FutureTask@694abf75 rejected from java.util.concurrent.ThreadPoolExecutor@15e7578d[Terminated, pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 0]
	at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2047)
	at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:823)
	at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1369)
	at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:112)
	at java.util.concurrent.Executors$DelegatedExecutorService.submit(Executors.java:678)
	at demo.MyService.afterPropertiesSet(TopoService.java:248)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1514)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1452)
	... 18 more

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
I submited this bug at JDK-8144799. But that issue has been closed. This time I find a  way to reproducing that issue.  

There has possible the ThreadPoolExecutor already finalize by jvm before submit Runnable instance? 
I wrote a test case like this: 

import java.util.concurrent.ExecutorService; 
import java.util.concurrent.Executors; 

public class A implements Runnable { 
    public void afterProperties() { 
        Executors.newSingleThreadExecutor().submit(new Runnable() { 
            @Override 
            public void run() { 
                System.out.println(Thread.currentThread().getName()); 
            } 
        }); 
    } 
    @Override 
    public void run() { 
        afterProperties(); 
    } 
    public static void main(String[] args) throws Exception { 
        ExecutorService execMain = Executors.newFixedThreadPool(2); 
        A testcase = new A(); 
        for (int i = 0; i < 1000; i++) { 
            execMain.submit(testcase); 
        } 
    } 
} 

The byte code for A.afterProperties: 
  public void afterProperties(); 
     0  invokestatic java.util.concurrent.Executors.newSingleThreadExecutor() : java.util.concurrent.ExecutorService [17] 
     3  new A$1 [23] 
     6  dup 
     7  aload_0 [this] 
     8  invokespecial A$1(A) [25] 
    11  invokeinterface java.util.concurrent.ExecutorService.submit(java.lang.Runnable) : java.util.concurrent.Future [28] [nargs: 2] 
    16  pop 
    17  return 
        
Most of the time, I think there have 1001 instance for ThreadPoolWorker. But when I dump this process's memory, use MAT checked the number of the ThreadPoolWorker. The count just some hundreds, less than 1000, and the ctl.value of the some instance is 1610612736. 
I think maybe invoke submit() method, the instanceof of ThreadPoolExecutor already finalized by jvm. I check the ThreadPoolExecutor.finalize(), when it invoked, the shutdown() method will be invoked. 
Then sumbit the A$1, the ctl status arleady "SHUTDOWN", so the exception message like "rejected from java.util.concurrent.ThreadPoolExecutor@25298878[Terminated, pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 0]". 




REPRODUCIBILITY :
This bug can be reproduced occasionally.

---------- BEGIN SOURCE ----------
Then I change the test case like this, and reproduce the exception


import java.util.concurrent.ExecutorService; 
import java.util.concurrent.Executors; 

public class A implements Runnable { 
    public void afterProperties() { 
        Executors.newSingleThreadExecutor().submit(new Runnable() { 
            @Override 
            public void run() { 
                System.out.println(Thread.currentThread().getName()); 
            } 
        }); 
    } 
    @Override 
    public void run() { 
        try { 
            afterProperties(); 
        } catch (Exception e) { 
            // TODO Auto-generated catch block 
            e.printStackTrace(); 
            System.exit(-1); 
        } 
    } 
    public static void main(String[] args) throws Exception { 
        ExecutorService execMain = Executors.newFixedThreadPool(2); 
        A testcase = new A(); 
        execMain.submit(new Runnable(){ 

            @Override 
            public void run() { 
                while(true) { 
                    System.gc(); 
                    try { 
                        Thread.sleep(2000); 
                    } catch (InterruptedException e) { 
                        // TODO Auto-generated catch block 
                        e.printStackTrace(); 
                    } 
                } 
            }}); 
        for (;;) { 
            execMain.submit(testcase); 
            Thread.yield(); 
        } 
    } 
}

---------- END SOURCE ----------


Comments
See in particular this discussion: http://cs.oswego.edu/pipermail/concurrency-interest/2006-March/002353.html Also in relation to 6399443 it states: 2. Add javadoc wording explaining the need to shut down ExecutorServices and in particular, those created using Executors.newFixedThreadPool. but I can not locate any such wording ??
16-12-2015

Doug Lea writes: See JDK-6399443 and concurrency-interest discussion in March 2006 with subject "Finalization changes to DelegatedExecutorService" http://cs.oswego.edu/pipermail/concurrency-interest/2006-March/thread.html Without this, some users were leaking Executors/Threads. This is arguably not our (j.u.c) problem. But after some resistance, we decided to add the finalizer. With it, users may encounter surprisingly early shutdown without reachabilityFence.
16-12-2015

Part of the problem is that there are two finalize methods in play here. The implementation does public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, so the outer executor may get finalized while methods on the inner are still executing. Not sure of the motivation for the finalization strategy here.
15-12-2015

Many have tried and failed to get us to where objects with non-trivial finalize methods cannot become unreachable while one of its methods is still executing. I'm still hoping we can strengthen https://docs.oracle.com/javase/specs/jls/se8/html/jls-12.html#jls-12.6.1 and JVM implementations. In the meantime, use the easy workaround of keeping a strong reference around until after submit completes.
14-12-2015

Attached Test case executed on following: JDK 8u66 - Fail JDK 8u72 - Fail JDK 9ea b93 - fail
14-12-2015

The executor implementation returned by newSingleThreadExecutor does have a finalize() method that will shutdown the executor. The test code doesn't retain the reference to the Executor and so it can become unreachable before the submit has executed. This kind of early finalization can be surprising but it is permitted by the specification and does occur in practice. The nature of the returned ExecutorService should be documented more clearly. A workaround would be to use the returned executor after the submit: ExecutorService es = Executors.newSingleThreadExecutor(); es.submit(new Runnable()); // this will start the thread in the executor and so keep everything reachable if (es.isTerminated()) // should ensure es can't become unreachable until after submit() completes throw new Error("unexpected"); Also note that JDK-8144799 was closed as "incomplete" which means it could be re-opened if more information was provided.
14-12-2015