多线程处理中静态成员的意外行为

时间:2014-09-17 08:09:56

标签: java multithreading static-members

我已经解决了随机多线程处理的问题。我很高兴,因为它有效,但我想知道为什么。以下代码中的错误成员称为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);
        }
    }

}

我对静态最终成员的理解是错误的吗?或者我的代码中还有其他东西不是线程安全的吗?

3 个答案:

答案 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);
}