【问题标题】:ThreadLocal initialValue() called multiple times per thread. why?每个线程多次调用 ThreadLocal initialValue()。为什么?
【发布时间】:2014-04-17 21:31:50
【问题描述】:

我们看到一个问题,因为 ThreadLocal 的 initialValue() 被多次调用。

private static class MonMetricsTLS extends ThreadLocal<IMonitor> {
    public MonMetricsTLS(MetricConfig config) {
        this.config = config;
        Timer timer = new Timer(true);
    }

    @Override
    protected IMonitor initialValue() {
            IMonitor mon = new MonitorImpl(config);
            timer.schedule(new SenderTimerTask(mon), config.senderPeriodMs(), config.senderPeriodMs());
            return mon;
    }
}

在第一次和第二次调用之间,计时器可能会变坏,我们会在 schedule() 中得到非法状态异常。

为什么每个线程会被多次调用?

【问题讨论】:

  • 你能告诉我们你是如何使用它的吗?
  • 这是实际代码吗?为什么构造函数中有多余的本地Timer?你在哪里初始化this.timer
  • 否则您在initialValue() 中引用的计时器必须在您的外部类中声明为静态。是这样吗?
  • constr 中的计时器不是多余的 - 它在初始值中使用
  • 我们不知道该调用在哪里使用,所以我们不知道是什么调用了它所覆盖的方法。请告诉我们您在哪里以及如何使用该类

标签: java thread-local


【解决方案1】:

这看起来您在多个线程中使用相同的Timer 对象。您应该将new Timer() 移动到initialValue()。只有这样,您才能确保每个线程都有一个唯一的 Timer 对象。根据您的代码猜测,config 是不可变的,因此您可以将this 传递给计时器。然后,您只需创建一个 MonMetrics 并在所有线程中重用它。

【讨论】:

  • 但是为什么你想要一个新的 Timer 每个线程是另一个问题。我会认为相当浪费。
  • 我没有在线程之间重用计时器。 @EJP - 是的,我们现在已经删除了它。这是一个旧代码库。
【解决方案2】:

看起来 java 线程本地不保证 initialValue() 将被调用一次! 所以最好假设它可以被调用任意次数。

【讨论】:

  • initialValue() 的整个点要被多次调用,每个线程至少调用一次。但是每个线程也不会多次调用它。 ThreadLocal 负责将任何提供的初始值存储在内部映射中,以便从映射而不是初始值中接受对该值的任何进一步访问。
  • 如果它是本地线程的“initialValue”,则每个线程至少应按函数名调用一次。特别是您提到的“不被多次调用”的保证不存在。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-27
  • 1970-01-01
  • 2019-09-08
  • 1970-01-01
  • 2017-09-24
相关资源
最近更新 更多