我正在阅读旧的Brian Goetz article(2008),内容涉及在servlet上下文中处理共享的可变数据时如何避免一些容易犯的错误。
他提到了Java内存模型(JMM)如何定义“ 保证读取变量的线程在一定条件下可以看到在另一个线程中写入结果的条件”,以及如何“ 发生在排序之前跨线程只能通过在公共锁上使用synchronizing或访问公共volatile变量来创建。“ [重点是我的]
(而JMM定义单个线程的“事前”排序是如何基于源代码中语句的排序的。)
本文继续介绍以下代码,作为处理会话数据的部分解决方案:
public PlayerScore getHighScore() {
ServletContext ctx = getServletConfig().getServletContext();
PlayerScore hs = (PlayerScore) ctx.getAttribute("highScore");
PlayerScore result = new PlayerScore();
result.setName(hs.getName());
result.setScore(hs.getScore());
return result;
}
public void updateHighScore(PlayerScore newScore) {
ServletContext ctx = getServletConfig().getServletContext();
PlayerScore hs = (PlayerScore) ctx.getAttribute("highScore");
if (newScore.getScore() > hs.getScore()) {
hs.setName(newScore.getName());
hs.setScore(newScore.getScore());
ctx.setAttribute("highScore", hs); // set-after-write!
}
}
上述代码末尾的“写后设置”行解决了一个常见问题:“可见性”问题,但不能解决其他与线程相关的问题(没有使用“ volatile”或“同步”中的代码。
我不明白的是[[如何,这解决了“可见性”问题(即,一个线程可能看到过时和/或不一致的数据版本的问题)。
本文解释:写后设置技术能够消除可见性问题,因为在排序之前发生的情况是可传递的,并且在updateHighScore()中对setAttribute()的调用与对getHighScore()中的getAttribute()。由于对HighScore状态的更新发生在setAttribute()之前,而setAttribute()发生在getAttribute()的返回之前,而getAttribute()的返回发生在getHighScore()的调用者使用该状态之前,所以传递性可以让我们得出结论,即getHighScore()至少与对setAttribute()的最新调用一样最新。
所以,我对上述内容的总结是:陈述(2)正确吗?对于updateHighScore()中的单个线程,不是相反吗?
并且如果这两个不同的方法被两个不同的线程访问,并且没有显式使用同步或易变性,因此语句(3)是正确的,因此,不能保证线程之间的执行顺序/协调如何?] >
我正在阅读Brian Brian的旧文章(2008),内容涉及在Servlet上下文中处理共享的可变数据时如何避免一些容易犯的错误。他提到了Java内存模型(JMM)...
如在该问题的某些评论中所指出的,答案的确是可以假定ServletContext
的实现可以实现某种形式的线程安全。两个示例:Tomcat使用ConcurrentHashMap,而Jetty似乎通过其ConcurrentHashMap
类使用包裹在AtomicReference
中的AttributesMap。
另一个帮助我澄清这一点的参考点是Brian Goetz的“ Java Concurrency in Practice”的以下部分: