ThreadPoolExecutor and finalization

Peter Levart peter.levart at gmail.com
Thu Nov 2 15:16:37 UTC 2017


On 11/02/2017 01:47 PM, David Holmes wrote:
> On 2/11/2017 7:26 PM, Peter Levart wrote:
>> On 11/02/2017 03:34 AM, David Holmes wrote:
>>> On 2/11/2017 3:46 AM, Peter Levart wrote:
>>>> On 11/01/17 13:34, David Holmes wrote:
>>>>> On 1/11/2017 10:20 PM, Peter Levart wrote:
>>>>>> On 11/01/17 10:04, David Holmes wrote:
>>>>>>> On 1/11/2017 6:16 PM, Peter Levart wrote:
>>>>>>>> On 11/01/17 02:49, David Holmes wrote:
>>>>>>>>> Hi Roger,
>>>>>>>>>
>>>>>>>>> On 31/10/2017 11:58 PM, Roger Riggs wrote:
>>>>>>>>>> Hi Peter,
>>>>>>>>>>
>>>>>>>>>> Only native resources that do not map to the heap 
>>>>>>>>>> allocation/gc cycle need any kind
>>>>>>>>>> of cleanup.  I would work toward a model that encapsulates 
>>>>>>>>>> the reference to a native resource
>>>>>>>>>> with a corresponding allocation/release mechanism as you've 
>>>>>>>>>> described here and in the
>>>>>>>>>> thread on zip.
>>>>>>>>>>
>>>>>>>>>> For cleanup purposes, the independence of each resource may 
>>>>>>>>>> improve robustness
>>>>>>>>>> by avoiding dependencies and opportunities for entanglements 
>>>>>>>>>> and bugs due to exceptions
>>>>>>>>>> and other failures.
>>>>>>>>>>
>>>>>>>>>> In the case of TPE, the native resources are Threads, which 
>>>>>>>>>> keep running even if they are
>>>>>>>>>> unreferenced and are kept referenced via ThreadGroups.
>>>>>>>>>> I don't know the Executor code well enough to do more than 
>>>>>>>>>> speculate, but would suggest
>>>>>>>>>> that a cleaner (or similar) should be registered for each 
>>>>>>>>>> thread .
>>>>>>>>>
>>>>>>>>> Threads are not native resources to be managed by Cleaners! A 
>>>>>>>>> live Thread can never be cleaned. A dead thread has nothing to 
>>>>>>>>> clean!
>>>>>>>>
>>>>>>>> Right, but an idle thread, waiting for a task that will never 
>>>>>>>> come since the only entry point for submitting tasks is not 
>>>>>>>> reachable (the pool), may be cleaned...
>>>>>>>
>>>>>>> cleaned? It can be interrupted if you know about it and find 
>>>>>>> locate it. But it will not be eligible for cleaning ala Cleaner 
>>>>>>> as it will always be strongly reachable.
>>>>>>
>>>>>> Ah I see what you meant before. Yes, tracking Thread object with 
>>>>>> a Cleaner does not have any sense. But tracking thread pool 
>>>>>> object with a Cleaner and cleaning (stopping) threads as a result 
>>>>>> makes sense...
>>>>>
>>>>> No, because live Threads will keep the thread pool strongly 
>>>>> reachable.
>>>>>
>>>>> If you manage to structure things such that the Threads don't keep 
>>>>> the pool strongly reachable then you risk having the pool cleaned 
>>>>> while still actively in use.
>>>>
>>>> Pool is actively in use when it is still reachable. Only in that 
>>>> case can new tasks be submitted. When it is not reachable any more, 
>>>> no new tasks can be submitted and it can be shutdown():
>>>>
>>>>      /**
>>>>       * Initiates an orderly shutdown in which previously submitted
>>>>       * tasks are executed, but no new tasks will be accepted...
>>>
>>> Didn't we already determine that a Cleaner can't call shutdown() 
>>> because that requires a strong reference it doesn't have?
>>
>> It can't call shutdown() on a tracked pool object, but it could do 
>> something that acted equivalently as shutdown().
>>
>>>
>>> I think Doug already summed this up. The existing finalize() is 
>>> pointless because when it could be called there is nothing left to 
>>> be "cleaned up". There's no need for any use of Cleaner (even if it 
>>> could do anything useful).
>>
>> There's no need for finalize() or Cleaner in existing TPE as is, I 
>> agree. But there could be a thread pool that would self-shutdown when 
>> it is of no use any more (either using finalize() or Cleaner). For 
>> example, here is such pool:
>>
>> public class CleanableExecutorService implements ExecutorService {
>>      private final ThreadPoolExecutor tpe;
>>
>>      public CleanableExecutorService(ThreadPoolExecutor tpe) {
>>          CleanerFactory.cleaner().register(this, tpe::shutdown);
>>          this.tpe = tpe;
>>      }
>>
>>      // implement and delegate all ExecutorService methods to tpe...
>> }
>
> Ah I see - the old "extra level of indirection" solution. :) The 
> Cleaner keeps the tpe strongly reachable, but as soon as the holder 
> class becomes "unreachable" the Cleaner will shutdown the tpe.
>
> Though if you plan on abandoning a TPE such that you can't shutdown 
> directly, then you may as well just configure it so all the threads 
> terminate and it will "self-clean".

True, but if I for some reason want to use a long keepAliveTime, the TPE 
will hang there unused for such amount of time after all executing tasks 
are already finished before idle threads finally terminate. Suppose I 
want to use TPE in a library where I don't have control over the 
life-cycle of the program and such library is used in an app deployed in 
app server. Each redeployment of such app will keep threads alive for 
more time than necessary. Frequent redeployment (such as when developing 
the app) might pile up more threads than available...

Regards, Peter

> Cheers,
> David
>
>> Regards, Peter
>>
>>>
>>> David
>>



More information about the core-libs-dev mailing list