Отключение бездействующего потока Java ThreadPoolExecutors — вызов пользовательского кода очистки

Согласно документу ThreadPoolExecutor (Java ThreadPoolExecutor ), если я создам службу-исполнитель следующим образом:

new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);

и когда #threads > corePoolSize, простаивающие потоки будут уничтожены. Я хотел вызвать определенный код очистки приложения, когда ThreadPoolExecutor убивает любой поток. Я не смог получить четкий способ сделать это. Цените любую помощь. Заранее спасибо.


person Vaibhav Gumashta    schedule 03.08.2014    source источник
comment
Рассматривали ли вы возможность предоставить Executor свою собственную реализацию ThreadFactory, которая, в свою очередь, создает вашу собственную реализацию Thread, определяющую, что я был убит?   -  person Fildor    schedule 03.08.2014
comment
См. также WeakReference   -  person Fildor    schedule 03.08.2014
comment
@Fildor Да, я предоставил свою собственную фабрику, которая создала бы новый поток (который расширяет класс Thread), который переопределяет метод прерывания, вызывая пользовательский код очистки, а затем вызывая метод прерывания суперкласса. Но похоже, убивая поток, ThreadPoolExecutor не вызывает метод прерывания.   -  person Vaibhav Gumashta    schedule 03.08.2014
comment
Я только что посмотрел на реализацию и думаю, что переопределение interrupt для запуска очистки — это не то, что вам нужно. Когда рабочий поток уничтожается, ThreadPool просто отказывается от ссылки, но не обязательно вызывает прерывание. И наоборот: когда вызывается прерывание, это не означает, что поток уничтожается.   -  person Fildor    schedule 03.08.2014
comment
Я предполагаю, что ваша ThreadFactory должна хранить WeakReferences для потоков, которые она создает, и регистрировать их с помощью ReferenceQueue. Там они ставятся в очередь, когда становятся слабо достижимыми (все сильные ссылки отбрасываются). Вот что я бы попробовал. Может быть, у кого-то есть лучшая (более простая) идея?   -  person Fildor    schedule 03.08.2014
comment
@Fildor Похоже, это лучшая идея. Позвольте мне попробовать это и обновить поток. Спасибо!   -  person Vaibhav Gumashta    schedule 03.08.2014
comment
Я никогда не нуждался в этом, что заставляет меня подозревать, что у вас проблема с дизайном.   -  person djechlin    schedule 03.08.2014


Ответы (1)


правильный способ сделать это — расширить Thread и запустить код очистки после возврата метода run(), например:

    BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(50);
    ThreadFactory threadFactory = new ThreadFactory() {
        @Override
        public Thread newThread(Runnable r) {
            return new Thread(r) {
                @Override
                public void run() {
                    super.run();
                    //DO YOUR CLEANUP HERE
                }
            };
        }
    };
    new ThreadPoolExecutor(1, 10, 10, TimeUnit.SECONDS, workQueue, threadFactory);

прерывание почти наверняка не будет вызвано в вашем сценарии, поскольку оно будет вызываться только при завершении работы активного исполнителя (и даже в этом случае не всегда).

вызывая очистку непосредственно после запуска, ваш код очистки вызывается прямо перед смертью потока (поток умирает после возврата метода run()).

person radai    schedule 03.08.2014
comment
Отличный ответ имеет только 1 недостаток: если вы действительно возьмете код и запустите его, в пуле останется только 1 поток. Это связано с предоставленной вами рабочей очередью ArrayBlockingQueue. Если вы измените его работу с SynchronousQueue, ответ будет идеальным! Теперь, почему это происходит? Это должен быть еще один вопрос о стеке (если это еще не...) - person shlomi33; 03.08.2014
comment
@ shlomi33 - stackoverflow.com/questions/19528304/ - person radai; 04.08.2014