代码之家  ›  专栏  ›  技术社区  ›  bepopov

无法在继承的Thread类中将值设置为Inheritable ThreadLocal字段

  •  1
  • bepopov  · 技术社区  · 7 月前

    不知何故,当我在创建继承Thread的类的对象时尝试为Inheritable ThreadLocal字段设置值时,我得到了奇怪的行为:当执行到达run()方法中的打印行时,我的对象没有我在调用构造函数时设置的值。同时,当我尝试运行一个继承我的类的线程时,它会看到我在创建previos时设置的值。

    我尝试运行以下代码:

    public class InheritableThreadLocalExample {
    
      public static void main(String[] args) {
        ThreadOne threadOne = new ThreadOne("user-one", "thread-one");
        threadOne.start();
        ThreadTwo threadTwo = new ThreadTwo("user-two", "thread-two");
        threadTwo.start();
      }
    
      public static class ThreadOne extends Thread {
    
        protected static final InheritableThreadLocal<String> USERNAME = new InheritableThreadLocal<>();
    
        public ThreadOne(String username, String threadName) {
          super(threadName);
          USERNAME.set(username);
        }
    
        @Override
        public void run() {
          System.out.println(Thread.currentThread().getName() + ". Username: " + USERNAME.get());
        }
      }
    
      public static class ThreadTwo extends ThreadOne {
        public ThreadTwo(String username, String threadName) {
          super(username, threadName);
        }
      }
    
    }
    

    输出为:

    thread-two. Username: user-one
    thread-one. Username: null
    

    为什么会这样?

    1 回复  |  直到 7 月前
        1
  •  3
  •   Holger    7 月前

    建设者 ThreadOne 由主线程执行,因此,语句 USERNAME.set(username); 在这个构造函数中设置主线程的值。但是主线程的可继承线程局部变量的实际值被复制到构造函数中 Thread ,即前面的 super(threadName); ,已经。

    因此,第一个线程继承了第一个线程之前的状态 USERNAME.set(用户名); 调用,而第二个线程继承调用后的状态;继承关系无关紧要,正如我们可以证明的那样:

    public class InheritableThreadLocalExample {
      public static void main(String[] args) {
        ThreadOne threadOne = new ThreadOne("user-one", "thread-one");
        printValue();
        threadOne.start();
        Thread threadTwo = new Thread(
                           InheritableThreadLocalExample::printValue, "thread-two");
         // things happening after Thread's constructor are irrelevant:
        ThreadOne.USERNAME.set("user-two");
        threadTwo.start();
      }
    
      static void printValue() {
        System.out.println(Thread.currentThread().getName()
                         + ". Username: " + ThreadOne.USERNAME.get());
      }
    
      public static class ThreadOne extends Thread {
        protected static final InheritableThreadLocal<String> USERNAME
                                      = new InheritableThreadLocal<>();
    
        public ThreadOne(String username, String threadName) {
          super(threadName);
          USERNAME.set(username);
        }
    
        @Override
        public void run() {
          printValue();
        }
      }
    }
    
    main. Username: user-one
    thread-one. Username: null
    thread-two. Username: user-one
    

    我们可以很容易地在主线程中设置预期的值,而不需要子类:

    public class InheritableThreadLocalExample {
      static final InheritableThreadLocal<String> USERNAME
                                                = new InheritableThreadLocal<>();
    
      public static void main(String[] args) {
        USERNAME.set("user-one");
        Thread threadOne = new Thread(
            InheritableThreadLocalExample::printValue, "thread-one");
        USERNAME.set("user-two");
        Thread threadTwo = new Thread(
            InheritableThreadLocalExample::printValue, "thread-two");
    
        // demonstrate that each thread has its own value
        // and main doesn't need a value:
        USERNAME.remove();
    
        threadOne.start();
        threadTwo.start();
      }
    
      static void printValue() {
        System.out.println(Thread.currentThread().getName()
                         + ". Username: " + USERNAME.get());
      }
    }
    
    thread-one. Username: user-one
    thread-two. Username: user-two
    
    推荐文章