【问题标题】:Do operations on ThreadLocal have to be synchronized?ThreadLocal 上的操作是否必须同步?
【发布时间】:2017-05-10 11:15:16
【问题描述】:

这是我偶然发现的代码:

class TransactionContextHolder {

private static final ThreadLocal<TransactionContext> currentTransactionContext = new NamedInheritableThreadLocal<TransactionContext>(
    "Test Transaction Context");


static TransactionContext getCurrentTransactionContext() {
    return currentTransactionContext.get();
}

static void setCurrentTransactionContext(TransactionContext transactionContext) {
    currentTransactionContext.set(transactionContext);
}

static TransactionContext removeCurrentTransactionContext() {
    synchronized (currentTransactionContext) {
        TransactionContext transactionContext = currentTransactionContext.get();
        currentTransactionContext.remove();
        return transactionContext;
    }
}

}

currentTransactionContext 字段是 ThreadLocal 类型,它是类中唯一的字段。

在我看来,这里不需要同步,因为存储在 ThreadLocal 中的值与特定线程相关联,因此它不是共享状态。此外,我认为它会影响性能,因为 currentTransactionContext 本身是共享的,并且只允许一个线程进入块,而许多线程可以并行执行而不影响正确性。

这里需要同步吗?

【问题讨论】:

  • 只要确保currentTransactionContext.initialValue(甚至是getter)没有共享状态,然后你就可以删除同步了。

标签: java multithreading performance synchronized thread-local


【解决方案1】:

一般来说,仅给定程序的一小部分 sn-p,就很难保证线程安全,因为线程安全是整个程序的属性,synchronized 可以协调多个程序的行为程序的不同部分。

例如:也许其他地方有一些其他代码使用疯狂的不安全反射来尝试检查和/或改变 ThreadLocal 的内部结构,因此如果您在没有锁定的情况下改变 ThreadLocal 就会中断?

不过,实际上,您是对的:没有任何理由在 ThreadLocal 实例上进行同步,除非可能在其 initialValue 方法内。 ThreadLocal 本身就是一种线程安全机制,它对线程安全的管理比你通过添加synchronized 得到的更好。

(向Margaret Bloom 致敬以指出initialValue 的情况。)

【讨论】:

  • 没错。我能想到的唯一竞争条件来源是ThreadLocal 的方法initialValue。这在所有线程之间共享,并且如链接文档中的示例所示,它必须使用一些保护。这个方法被get 调用,因为他们总是在它之后执行remove(不知道为什么)
  • @MargaretBloom:回复:“他们总是在它之后执行 remove(不知道为什么)”:好吧,OP 发布的是 removeCurrentTransactionContext 方法,所以我认为它使感觉它总是删除当前的事务上下文。
  • 对,我忽略了方法名称 :) remove 有道理,get 是可疑的。如果还没有上下文,该方法可能会创建一个新的,然后将其删除。但正如你所说:这只是一个小小的 sn-p。
  • @MargaretBloom:在这种特殊情况下,initValue 没有被覆盖,所以它设置为 null,但总的来说你是对的,我应该考虑 initialValue。
  • @ruakh:关于其他地方的类的变异状态,一个地方的同步块并不能保护你。您必须在也会改变状态的地方使用同步。例如,如果有人调用了setCurrentTransactionContext 方法并且它在currentTransactionContext.remove() 之后立即执行,那么它会使remove 方法返回过时的上下文值。但是在这种情况下,它是 ThreadLocal,setCurrentTransactionContextremoveCurrentTransactionContext 方法必须由同一线程在同一时间调用,这是不可能的。
猜你喜欢
  • 2013-02-15
  • 1970-01-01
  • 1970-01-01
  • 2010-09-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-06-08
  • 1970-01-01
相关资源
最近更新 更多