Понимание InheritableThreadLocal в Java - PullRequest
1 голос
/ 05 апреля 2020

Я хотел, чтобы ParentThread установил threadId в значение (скажем, p1), передаваемое его конструктору. Тогда для всех его потомков threadId установлено значение p1.c1, p1.c2 и так далее. Я написал следующий код:

public class InheritableThreadLocalDemo {

    public static void main(String[] args) 
    { 
        ParentThread pt = new ParentThread("p1"); 
        pt.start(); 
    } 
}

class ParentThread extends Thread { 
    static int childCount = 0;

    public static InheritableThreadLocal threadId = new InheritableThreadLocal() {
        public Object childValue(Object parentValue) 
        { 
            return parentValue.toString() + ".c" + (++childCount) ;  //this is line 18 where NullPointerException is occuring
        }
    }; 

    public ParentThread(String pThreadId) {
        threadId.set(pThreadId);
    }

    public void run() 
    { 
        System.out.println("Thread id:" + threadId.get());

        ChildThread childThread1 = new ChildThread(); 
        childThread1.start(); 

        ChildThread childThread2 = new ChildThread(); 
        childThread2.start();
    } 
} 
class ChildThread extends Thread { 

    public void run() 
    { 
        System.out.println("Child Thread Value :" + ParentThread.threadId.get()); 
    } 
} 

Он печатает:

Thread id:null
Exception in thread "Thread-0" java.lang.NullPointerException
    at com.mahesha999.examples.java.multithreading.ParentThread$1.childValue(InheritableThreadLocalDemo.java:18)
    at java.lang.ThreadLocal$ThreadLocalMap.<init>(Unknown Source)
    at java.lang.ThreadLocal$ThreadLocalMap.<init>(Unknown Source)
    at java.lang.ThreadLocal.createInheritedMap(Unknown Source)
    at java.lang.Thread.init(Unknown Source)
    at java.lang.Thread.init(Unknown Source)
    at java.lang.Thread.<init>(Unknown Source)
    at com.mahesha999.examples.java.multithreading.ChildThread.<init>(InheritableThreadLocalDemo.java:37)
    at com.mahesha999.examples.java.multithreading.ParentThread.run(InheritableThreadLocalDemo.java:30)

Я отладил в eclipse и обнаружил, что внутри конструктора threadId правильно установлено значение p1, но внутри ParentThread.run(), это читается как null. Также внутри childValue, .toString() вызывается на null, бросая NullPointerException.

Q1. * Почему значение, установленное в конструкторе, не отображается внутри run() и childValue()?

Также я хочу, чтобы это ParentClass можно было использовать повторно, то есть Я должен иметь возможность иметь такие серии, как

  • p2.c1, p2.c2 и т. Д.
  • p3.c1, p3.c2 и т. Д.
  • и и так далее

Мне кажется, для этого я должен сделать childCount и threadId non-stati c. Но удаление static из обеих этих переменных дает мне ошибку времени компиляции

Cannot make a static reference to the non-static field ParentThread.threadId

внутри ChildThread.run() для ParentThread.threadId.get(). Но теперь я не могу представить, как сделать эти InheritableThreadLocal переменными уникальными для каждого потока в каждом экземпляре.

Q2. Как мне это сделать?

Q3. Или здесь что-то не так глупо? Является ли «уникальным для каждого потока на экземпляр» не что иное, как «уникальным только для каждого экземпляра», и нам вообще не нужна концепция локального потока в этом случае? Поэтому все ThreadLocal переменные должны быть static по соглашению?

Ответы [ 2 ]

1 голос
/ 05 апреля 2020

Я думаю, что ваше заблуждение заключается в том, что ThreadLocal связан с объектом Thread, в который вы его встраиваете. Он связан с тем, что текущий поток вызывает при вызове локальных методов потока. У вас есть 4 темы:

  1. Основная тема
  2. Родительская тема
  3. Дочерняя тема # 1
  4. Дочерняя тема # 2

Конструктор, который вызывает метод локального набора потока для Родительского объекта, вызывается в основном потоке (# 1), устанавливая локальный поток для , чтобы нить не была родительским потоком (# 2), поскольку он еще не запущен.

Вот минимальный рабочий пример без переопределения чего-либо в классе потока:

package foo;

import java.util.concurrent.atomic.AtomicInteger;

public class InheritableThreadLocalDemo {

    public static void main(String[] args) {

        final AtomicInteger childCount = new AtomicInteger();

        final InheritableThreadLocal<String> local = new InheritableThreadLocal<String>() {
            public String childValue(String parentValue) {
                return parentValue.toString() + ".c" + (childCount.incrementAndGet());
            }
        };

        final Runnable child = () -> {
            System.out.println("Child Thread Value: " + local.get());
        };

        final Runnable parent = () -> {
            // The thread local value is associated with the thread that is running this block of
            // code.
            local.set("p1");
            System.out.println("Thread id:" + local.get());

            Thread c1 = new Thread(child);
            c1.start();

            Thread c2 = new Thread(child);
            c2.start();
        };

        final Thread parentThread = new Thread(parent);
        parentThread.run();
    }
}

В этом примере есть локальный экземпляр одного потока, который совместно используется дочерний и родительский Runnables. Под капотом эти лямбды хранят локально ссылающийся threadLocal как поле члена, которое вы можете проверить во время отладки. Но предметный аспект этого отдельн.

Думайте о локальном потоке как о ключе в структуре карты для каждого потока.

class Thread {
    Map<ThreadLocal, Object> threadLocals;
    // getter
    Map<ThreadLocal, Object> getThreadLocals() { return threadLocals; }
}

Каждый отдельный экземпляр ThreadLocal сам по себе является ключом к этой структуре. Текущий поток определяется как результат вызова Thread.currentThread (). Итак, подумайте каждый раз, когда вы вызываете threadLocalInstance.get (), он делает это:

Object thisThreadValue = Thread.currentThread().getThreadLocals().get(threadLocalInstance);
0 голосов
/ 05 апреля 2020

Давайте посмотрим на соответствующие разделы кода.

class ParentThread extends Thread { 
    // ...
    public ParentThread(String pThreadId) {
        threadId.set(pThreadId);
    }

Так что threadId будет установлено для Thread.currentThread(), который запускает конструктор. Это не может быть создаваемый экземпляр.

    public void run() 
    { 
        System.out.println("Thread id:" + threadId.get());

Теперь в предположительно новом потоке threadId не будет инициализирован.

        ChildThread childThread1 = new ChildThread(); 

А здесь null разыменованный.

Как правило, я бы избегал ThreadLocal и большинства других вещей, связанных с глобальными переменными. InheritableThreadLocal Я бы побежал с криком, хотя это обеспечило развлечение.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...