ThreadLocal 与新局部变量的真正优势
Real advantage of ThreadLocal versus new local variable
我意识到 ThreadLocal 已被访问过多次,尤其是在 SimpleDateFormat 示例中。
但似乎即使通过创建 SDF 'ThreadLocal',我们仍然为每个线程创建一个 SDF() 实例,这相当于调用一个新的 SDF()。
这样做一次会给每个线程一份 SDF 副本 -
ThreadLocal<SimpleDateFormat> sdf = new ThreadLocal<SimpleDateFormat>();
这也是 -
SimpleDateFormat sdf = new SimpleDateFormat();
线程安全是通过每次复制SDF来简单创建的。
所以我们在两种情况下都有相同数量的 SDF 实例。
那么,实例数量的提升在哪里?
如果有 none - 为什么我要使用 ThreadLocal,而不是每次都在线程中执行 new SDF()?这样我也不用担心同步问题。
如果这个论点是正确的 - 我看到使用 ThreadLocal 的一个原因是它的范围。它可以是 .get() 稍后在代码的其他地方,而不是在参数中传递它。
我想确认一下,我理解正确。
谢谢。
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
格式化程序。
我意识到 ThreadLocal 已被访问过多次,尤其是在 SimpleDateFormat 示例中。
但似乎即使通过创建 SDF 'ThreadLocal',我们仍然为每个线程创建一个 SDF() 实例,这相当于调用一个新的 SDF()。
这样做一次会给每个线程一份 SDF 副本 -
ThreadLocal<SimpleDateFormat> sdf = new ThreadLocal<SimpleDateFormat>();
这也是 -
SimpleDateFormat sdf = new SimpleDateFormat();
线程安全是通过每次复制SDF来简单创建的。 所以我们在两种情况下都有相同数量的 SDF 实例。
那么,实例数量的提升在哪里?
如果有 none - 为什么我要使用 ThreadLocal,而不是每次都在线程中执行 new SDF()?这样我也不用担心同步问题。
如果这个论点是正确的 - 我看到使用 ThreadLocal 的一个原因是它的范围。它可以是 .get() 稍后在代码的其他地方,而不是在参数中传递它。
我想确认一下,我理解正确。
谢谢。
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
格式化程序。