你应该在使用Java之前克隆Date对象吗?

时间:2017-12-13 15:20:45

标签: java spring hibernate

我听说Date不是线程安全的问题,但是当这可能结束时,没有找到任何实际的解释。我知道解析时DateFormat结尾很糟糕,但是Date

所以作为一个例子,使用Date如下所示最终会出现一些令人讨厌的多线程环境?

@Transactional(rollbackFor = {Throwable.class})
public void moveToArchive(UserContext userContext, long id) throws PostFailedException {
        PostEntity postEntity;
        try {
            postEntity = postDAO.get(userContext.getUserId(), id);
        } catch (EntityNotFoundException ex) {
            throw new PostFailedException(PostFailedException.ITEM_NOT_FOUND, ex);
        }
        //...
        // copying java.util.Date between entities
        archivedPostEntity.setCreationDate(post.getCreationDate());
        //...
}

如果getCreationDate只返回内部属性,那会不好?或者Spring / Hibernate会正确处理这个问题吗?

getCreationDate应该是什么样的?

这样的事情很好吗?

@Entity
public class PostEntity implements Post {
//...
    @NotNull
    @Temporal(value = TemporalType.TIMESTAMP)
    private Date postDate;
//... 
    @Override
    public Date getPostDate() {
        return postDate;
    }
}

或许它应该更像这样?

@Entity
public class PostEntity implements Post {
//...
    @NotNull
    @Temporal(value = TemporalType.TIMESTAMP)
    private Date postDate;
//... 
    @Override
    public synchronized Date getPostDate() {
        return new Date(postDate.getTime());
    }
}

2 个答案:

答案 0 :(得分:3)

Java Date对象不是线程安全的。您通过getPostDate()方法创建新Date的方法将在很大程度上解决此问题。事实上,它被认为是最佳做法。

现在无论线程安全如何,建议您在从实体返回时重新创建(克隆或其他)Date对象。 FindBugs实际上会在你的POJO中寻找这个问题。原因是Date不像String类那样是可变的。

因此,从实体bean返回实时引用允许实体内的Date对象在不使用实体中相应的set方法的情况下进行变更。实体将不知道其内部状态是否已更改以及是否需要将其持久保存到数据存储。这种缺陷比你想承认的更频繁发生。

希望这能回答你的问题。

答案 1 :(得分:1)

  

我听说Date不是线程安全的问题,但是   当这可能结束时,没有找到任何实际的解释。

也许你的搜索范围过于狭窄。 Date不是线程安全的,在相同的上下文中会出现与任何其他不可线程的可变类相同的问题,它提供了相同的解决方案。

  

我   知道何时使用DateFormat解析结果很糟糕,但是Date

Date个实例是可变的。如果一个线程在线程之间共享,并且在没有适当的外部同步的情况下进行了突变,那么程序的行为就没有明确定义。

  

所以作为一个例子,使用Date如下所示最终会出现令人讨厌的事情   对于多线程环境? [...]

如果再次允许相同的Date实例被不同的线程访问,并且如果这些实例中的至少一个在实例化之后修改该实例,那么它可以这样做。然后,不同的线程可能会获得Date值的不一致视图,这可能会影响应用程序,从而导致其他不一致。

  

如果getCreationDate只返回内部属性,那么这是不是很糟糕?要么   Spring / Hibernate会正确处理吗?

Spring和Hibernate不会在这个领域自动提供线程安全性。事实上,它们可能会很好地促进您的实体,从而在线程之间共享其属性的值。在这种程度上,它们是问题的一部分。 Hibernate本身是线程安全的,但这还不够。

  

getCreationDate应该是什么样的? [...]

您在询问是否在实体实施中使用防御性复制,特别是在您的财产获取者中。防御性复制主要是保护您不使用实体自己的方法修改实体的内部状态。在这种特殊情况下,它还将最小化 - 但不是消除 - 线程之间共享的Date个实例。这些提供了一些保护,但它们绝不是一个完整的解决方案。

要完全消除因具有Date类型字段的实体所引起的问题,您需要在getter和setter中执行防御性复制,并且还必须同步这些防御性复制操作。但是您不需要在整个实体上进行同步,如果您有多个需要这种处理的属性,那么避免这种情况可能会提高性能。例如,

@Entity
public class PostEntity implements Post {
//...
    @NotNull
    @Temporal(value = TemporalType.TIMESTAMP)
    private Date postDate = new Date();
    private final Object postDateMonitor = new Object();
//... 

    @Override
    public Date getPostDate() {
        synchronized (postDateMonitor) {
            return new Date(postDate.getTime());
        }
    }

    @Override
    public void setPostDate(Date date) {
        synchronized (postDateMonitor) {
            postDate = new Date(date.getTime());
        }
    }
}