ThreadLocal与新本地变量

时间:2017-08-15 21:01:16

标签: java multithreading thread-local

我意识到已多次访问ThreadLocal,特别是使用SimpleDateFormat示例。

但似乎即使通过制作SDF' ThreadLocal',我们仍然在每个线程中创建一个SDF()实例,这相当于调用一个新的SDF()。

这样做一次就会给每个线程提供一份SDF -

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

这也是 -

SimpleDateFormat sdf = new SimpleDateFormat();

通过每次制作SDF副本,可以简单地创建线程安全性。 因此,在这两种情况下,我们都有相同数量的SDF实例。

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

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

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

我想确保,我正确理解这一点。

谢谢。

1 个答案:

答案 0 :(得分:3)

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格式化程序。