【问题标题】:Real advantage of ThreadLocal versus new local variableThreadLocal 与新局部变量的真正优势
【发布时间】:2017-08-15 21:01:16
【问题描述】:

我意识到 ThreadLocal 已被访问过多次,尤其是 SimpleDateFormat 示例。

但似乎即使将 SDF 设置为“ThreadLocal”,我们仍然为每个线程创建一个 SDF() 实例,这相当于调用了一个新的 SDF()。

这样做一次会给每个线程一个 SDF 副本 -

ThreadLocal<SimpleDateFormat> sdf = new ThreadLocal<SimpleDateFormat>();

这也是 -

SimpleDateFormat sdf = new SimpleDateFormat();

线程安全只是通过每次复制 SDF 来创建的。 所以我们在两种情况下都有相同数量的 SDF 实例。

那么,在实例数量方面的改进在哪里?

如果没有 - 为什么我要使用 ThreadLocal,而不是每次都在线程中执行 new SDF()?这样我也不担心同步。

如果这个论点是正确的 - 我看到使用 ThreadLocal 的一个原因是它的范围。它可以稍后在代码中的其他地方使用 .get(),而无需在参数中传递它。

我想确认一下,我理解正确。

谢谢。

【问题讨论】:

  • SimpleDateFormatter 是什么意思? JDK中没有这样的类。可能是SimpleDateFormat?但是关于SimpleDateFormat,你的意思是什么线程安全?
  • 抱歉,我现在修正了错字 - SimpleDateFormat,就是这样。
  • @Ivan Pronin SimpleDateFormat 不是线程安全的(我认为,Oracle 最近修复了它)。让多个线程使用一个公共静态最终 SDF 可能会导致意外结果。
  • 好吧,我是说每个线程只做一个新的 SDF()。不是跨线程共享的最终静态 SDF。 ThreadLocal SDF() 与 new SDF() 相比如何创建 new() 实例。
  • 请注意,DateSimpleDateFormat 等类现在是旧版。它们被java.time 包中的新日期API 取代。

标签: java multithreading thread-local


【解决方案1】:

ThreadLocals 有多种用途,但在包装非线程安全对象的上下文中,请考虑以下示例。
非线程本地方法很可能会崩溃,而线程本地版本不会。

public static void main(String... args) {
    MyParser parser = new MyParser();
    IntStream.range(0, 100).parallel().forEach(i-> parser.parseTl("20170816"));
    IntStream.range(0, 100).parallel().forEach(i-> parser.parse("20170816"));
}

static class MyParser {
    private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
    private static final ThreadLocal<SimpleDateFormat> sdftl =
            ThreadLocal.withInitial(()-> new SimpleDateFormat("yyyyMMdd"));

    public Date parse(String str) {
        try {
            return sdf.parse(str);
        } catch (ParseException e) {
            throw new RuntimeException(e);
        }
    }

    public Date parseTl(String str) {
        try {
            return sdftl.get().parse(str);
        } catch (ParseException e) {
            throw new RuntimeException(e);
        }
    }
}

现在您可以在每次要解析日期时简单地构造一个新的格式化程序:

public Date parse(String str) {
    try {
        return new SimpleDateFormat("yyyyMMdd").parse(str);
    } catch (ParseException e) {
        throw new RuntimeException(e);
    }
}

但这种方法的问题在于构造SimpleDateFormat 是一个相对较慢的操作,因此您希望构造它的次数最少。

在这种情况下当然还有其他选项,您可以同步对格式化程序的访问,但这可能比每个线程拥有自己的副本要慢得多。 或者您可以使用线程安全的 Java 8 java.time 格式化程序。

【讨论】:

    猜你喜欢
    • 2012-04-24
    • 1970-01-01
    • 2016-11-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-21
    • 2014-02-12
    相关资源
    最近更新 更多