This was discovered by my colleague Murray McDonald. I'll quote him rather than trying to paraphrase.
There are two “submit” methods in the org.geotools.swing.DefaultRenderingExecutor class. The first method renders the layers consecutively in a single thread. The second method renders the layers concurrently each in its own thread. For each task/thread that is created a “TaskInfo” object is constructed and added to a copyOnWriteArrayList “currrentTasks” container.
In our LayeredMapPane object we are now using the second submit method to take advantage of the concurrent rendering.
The RenderingExecutor initiates a single thread that uses the “currentTasks” structure in conjunction with a countdown latch to determine when a render is completed. The code that does this is in the pollTaskResult() method.
This code has a small, but significant bug in it because it is not properly removing TaskInfo elements from the currentTasks list. This leads to a memory leak and wasted cycles iterating over previously processed entries in the list.
Murray has a fix for this, it's a one-liner, that I'll submit a pull request for shortly.
Windows 10, Java 8