Пул потоков и InheritedThreadLocal

Я видел приведенный ниже вопрос. Ответ заключался в том, чтобы вместо этого использовать семафоры. Это не ответило на одну из других проблем, указанных в вопросе, с которым я столкнулся.

Использование InheritableThreadLocal с ThreadPoolExecutor -- или -- ThreadPoolExecutor, который не использует повторно потоки< /а>

У меня есть родительский поток, который устанавливает некоторый уникальный идентификатор для каждого нового запроса в InhertiedThreadLocal и отправляет 2 Runnable Tasks в ThreadPool, т.е. для 2 потоков. Для исходного запроса значения, установленные для InheritedThreadLocal в родительском потоке, правильно распространяются в ChildThread. Для следующих запросов дочерние потоки не получают последний InheritedThreadLocal, установленный родительским потоком, и используются старые значения в ChildThread.

Это связано с тем, что пул потоков повторно использует потоки, а InheritedThreadLocal копируется только при создании нового потока.

Теперь, как я могу распространить последнее значение InheritedThreadLocal от родительского к дочернему потоку в сценарии пула потоков. Есть ли выход для этого?


person crackerplace    schedule 08.08.2013    source источник
comment
Я пробовал гуглить, но не смог найти конкретное «да», если это можно решить.   -  person crackerplace    schedule 08.08.2013
comment
Я столкнулся с той же проблемой. Вы наконец нашли решение этой проблемы? Если да, добавьте его в качестве ответа.   -  person rahulmohan    schedule 05.01.2015
comment
@rahulmohan Каким-то образом мы не выполнили вышеуказанное требование и, следовательно, больше не пробовали. /a> проверьте это, если это вам поможет.   -  person crackerplace    schedule 06.01.2015
comment
Дело в том, что только когда создается дочерний поток, локальные значения потока родительских потоков будут переданы дочернему потоку. Как только этот дочерний поток возвращается в пул после первоначального выполнения или выполняется несколько раз оттуда, он не может получить доступ к контексту родительского потока .   -  person crackerplace    schedule 06.01.2015
comment
securecoding.cert.org/confluence/display/java/   -  person crackerplace    schedule 06.01.2015


Ответы (2)


Я написал эти методы по мере необходимости.

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.concurrent.ThreadPoolExecutor;

public class EnvUtils {
    /**
     * Extract the current inheritableThreadLocals map from the current thread.
     * Typical usage is in a threadpool, where:
     * <ul>
     * <li>You run {@link EnvUtils#extract()} in the running thread, to store
     * the information somewhere
     * <li>You create a method {@link ThreadPoolExecutor#beforeExecute()} in which
     * you run {@link EnvUtils#copy(Object)} with the above-stored information.
     * </ul>
     *
     * @return The current inheritableThreadLocals of the current thread
     */
    public static Object extract() {
        Object toreturn = null;
        try {
            // get field descriptor
            Field inthlocalsField = Thread.class.getDeclaredField("inheritableThreadLocals");
            inthlocalsField.setAccessible(true);
            //the object stored there
            Object inthlocalsMap = inthlocalsField.get(Thread.currentThread());
            // no need to copy, it will be done by the copy() method below
            toreturn = inthlocalsMap;
        } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
            // This may happen in a different Java implementation
            throw new RuntimeException(e);
        }
        return toreturn;
    }

    /**
     * Replaces the field inheritableThreadLocals of the current thread with the values provided.<br/>
     * It is the same as if the current thread was just created from the thread whose <code>stored</code>
     * values come from.<br/>
     * Must be called in the thread which want to inherit from given {@link inheritableThreadLocals} map.<br/>
     * <b>Note 1:</b> This does not modify non-inheritable thread locals<br/>
     * <b>Note 2:</b> This delete all previous values of {@link inheritableThreadLocals} in the current thread<br/>
     *
     * @param stored
     *            The stored inheritableThreadLocals value, coming from the extract() method
     */
    public static void copy(final Object stored) {
        try {
            // find ThreadLocalMap class
            String threadLocalClassName = ThreadLocal.class.getName();
            Class<?> threadLocaLMapClass = Class.forName(threadLocalClassName + "$ThreadLocalMap");
            // check that given object is an instance of the class
            if (stored == null || !threadLocaLMapClass.isInstance(stored)) {
                throw new IllegalArgumentException("Given object is not a ThreadLocalMap: " + stored);
            }
            // get constructor of ThreadLocalMap
            Constructor<?> creator = threadLocaLMapClass.getDeclaredConstructor(threadLocaLMapClass);
            creator.setAccessible(true);
            // get field descriptor of the thread
            Field inthlocalsField = Thread.class.getDeclaredField("inheritableThreadLocals");
            inthlocalsField.setAccessible(true);
            // create new inherited map
            Object newObj = creator.newInstance(stored);
            // set it to the current thread
            inthlocalsField.set(Thread.currentThread(), newObj);

        } catch (ClassNotFoundException | NoSuchMethodException | SecurityException | NoSuchFieldException
                | IllegalAccessException | IllegalArgumentException | InvocationTargetException
                | InstantiationException e) {
            // This may happen in a different Java implementation
            throw new RuntimeException(e);
        }

    }

}

EnvUtils.extract() возвращает ссылку на карту inheritableThreadLocals текущего потока.

Теперь, когда вы создаете Runnable для вызова в ThreadPool, просто сохраните в поле inheritableThreadInfo = EnvUtils.extract(), а в его методе run() просто вызовите EnvUtils.copy(inheritableThreadInfo).

Примечание. В этом решении много размышлений, и поэтому оно зависит от реализации Java. Я тестировал на Oracle Java 1.8.

person Arglanir    schedule 13.12.2016

Если мы настроим ThreadPoolExecutor следующим образом

new ThreadPoolExecutor(0, 2, 0L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());

он будет создавать новый поток каждый раз, поэтому InheritableThreadLocals будет унаследован, хотя тогда его вряд ли можно назвать ThreadPoolExecutor

person Evgeniy Dorofeev    schedule 08.08.2013
comment
Хорошо. Да, в этом случае это не будет пул потоков. Дело в том, что мы не используем ThreadPoolExecutor для управления потоками. Мы используем реализацию IBM javax.resource.spi.work.WorkManager для управления потоками. - person crackerplace; 08.08.2013
comment
Чтобы добавить еще один момент, вопрос можно упростить: есть ли какой-либо способ, которым мы можем получить InhertiedThreadLocals из контекста родительского потока в контекст дочернего потока всякий раз, когда поток из пула потоков выполняет данный исполняемый объект? - person crackerplace; 08.08.2013