我已经解决了随机多线程处理的问题。我很高兴,因为它有效,但我想知道为什么。以下代码中的错误成员称为INPUT_SDF
。我认为静态最终成员不需要同步块,但是当我删除它时,一切都会出错。
public class A implements Comparable<A>
{
public static final SimpleDateFormat INPUT_SDF = new SimpleDateFormat("EEE MMM dd yyyy HH:mm:ss", Locale.US);
...
public void setDate(String string) throws ParseException
{
synchronized (INPUT_SDF)
{
date = INPUT_SDF.parse(string);
}
}
}
我对静态最终成员的理解是错误的吗?或者我的代码中还有其他东西不是线程安全的吗?
答案 0 :(得分:3)
没有使字段静态最终不足以使代码线程安全。它仅使对象线程的引用赋值安全,从而确保其他线程将在该字段中看到相同的对象引用。它不会使存储在字段(分配后)线程安全的对象中存储的数据发生变异。这是SimpleDataFormat的问题。
来自SimpleDateFormat的文档。
日期格式未同步。建议为每个线程创建单独的格式实例。如果多个线程同时访问格式,则必须在外部进行同步。
如果SimpleDateFormat是无状态的,或者编写为使其状态为线程安全,那么yes static final就足以保证线程安全。
实际上,如果想要在线程之间共享同一个SimpleDataFormat实例,则必须首先将线程与同一个监视器同步。否则,建议每个线程创建一个新的SimpleDateFormat实例,或者根据需要创建一个新实例,或者使用ThreadLocal或类似的机制。
答案 1 :(得分:2)
班级SimpleDateFormat
未同步。
您可以查看此question了解详情。 希望它有所帮助。
答案 2 :(得分:2)
制作字段static final
只会使引用线程安全;它不会同步访问引用的对象。
由于SimpleDateFormat的实例不是线程安全的,如果多个线程同时使用实例,则必须同步对它的访问(就像你一样)。
使字段static final
保证所有线程都会看到引用的相同值。
但是,您当前的代码是一个潜在的瓶颈,因为所有线程必须排队才能使用该实例,可能会产生比每次创建新实例更差的性能。
如果您想安全地重用SimpleDateFormat的实例,请考虑使用ThreadLocal
方便地为每个线程使用单独的实例,这样您就可以删除同步:
private static final ThreadLocal<SimpleDateFormat> formats =
new ThreadLocal<SimpleDateFormat>() {
@Override protected SimpleDateFormat initialValue() {
return new SimpleDateFormat("EEE MMM dd yyyy HH:mm:ss", Locale.US);
}
};
public void setDate(String string) throws ParseException {
return formats.get().parse(string);
}