volatile应该用于Java web应用程序中域模型类的属性吗?
以下是我的想法: 尽管HTTP请求周期基本上由“单个线程”处理,但每次为同一会话处理HTTP请求时,它都可能由线程池中的不同线程处理 如果没有在域模型对象上使用volatile关键字(其生命周期跨越同一会话的多个HTTP请求),那么根据我的理解,该属性不可能在为第一个HTTP请求提供服务的线程中进行线程本地缓存(由编译器进行优化)吗?如果第二个HTTP请求由另一个线程提供服务,那么第二个线程可能看不到第一个线程对该属性所做的更改volatile应该用于Java web应用程序中域模型类的属性吗?,java,wicket,volatile,Java,Wicket,Volatile,以下是我的想法: 尽管HTTP请求周期基本上由“单个线程”处理,但每次为同一会话处理HTTP请求时,它都可能由线程池中的不同线程处理 如果没有在域模型对象上使用volatile关键字(其生命周期跨越同一会话的多个HTTP请求),那么根据我的理解,该属性不可能在为第一个HTTP请求提供服务的线程中进行线程本地缓存(由编译器进行优化)吗?如果第二个HTTP请求由另一个线程提供服务,那么第二个线程可能看不到第一个线程对该属性所做的更改 这是否意味着“危险威尔·罗宾逊”?还是我遗漏了关于volatile
这是否意味着“危险威尔·罗宾逊”?还是我遗漏了关于volatile关键字使用(或不使用)的一个关键点?我认为您对它的理解是正确的。根据你的描述,我认为应该使用它。如果它不仅仅是一个基本类型,我宁愿同步 有关volatile的良好信息:
是,如果在不同线程之间共享对象,则可能存在争用条件。如果没有“发生在之前”关系,则一个线程的写操作可能不会被另一个线程中的读操作看到 在一个线程中执行易失性写操作,在另一个线程中执行同一字段的易失性读操作,可以在两个线程之间建立“发生在之前”关系,并确保写操作的可见性
这是一个复杂的问题,简单地使用volatile关键字可能不是一个好的解决方案 我认为您忘记了处理HTTP请求的线程首先需要从应用服务器提供的
HttpSession
中检索域模型对象的实例。您描述的场景中的线程处理请求2还没有此域模型的实例-它必须在处理每个请求开始时从会话实现中检索它
我认为,假设应用服务器中的会话处理实现以避免内存模型可见性问题的方式处理会话数据是完全合理的。ApacheTomcat的默认设置(非集群)
添加volatile对我来说似乎完全没有必要。在我所从事的任何项目中,我从未见过在Servlet环境中对由HTTP请求处理的域模型对象这样做
如果thread-1和thread-2在处理两个不同的请求时模拟引用了同一个对象实例,并且您担心在处理请求时一个线程中的更改对另一个线程可见,那么情况就不同了,但这听起来不像你在问什么。如果你在会话中有一个可变对象,那就是麻烦。但通常情况下,解决方案不是保护单个字段;而是应该交换整个对象 假设会话中有用户对象。大多数请求只是检索、读取和显示它 有一个可以修改用户信息的请求。检索用户对象、修改它是一个非常糟糕的主意。最好创建完整的新用户对象,并将其插入会话
在这种情况下,User中的字段不需要任何保护;线程安全由session setAttribute()-getAttribute()保证。如果您有并发问题,只添加“volatile”可能对您没有帮助 至于将对象保留为会话的属性,我建议您只保留对象的ID,并在需要时使用它来检索“活动”实例(如果使用Hibernate,连续检索将返回相同的对象,因此这不会导致性能问题)。将这个特定对象的所有修改逻辑封装到一个façade中,并使用dababase锁定在那里进行并发控制 或者,如果您真的、真的、真的想使用基于内存的锁定,并且确实确定在集群中永远不会有两个应用程序实例运行,那么请确保您的façade逻辑在正确的级别上同步。如果您的同步过于细粒度(低级操作,如volatile变量),则可能不足以使代码线程安全。例如,
java.util.Hashtable
是完全同步的,但是如果您有这样的逻辑,它并不意味着什么:
01 if (!hashtable.containsKey(key)) {
02 hashtable.put(key, calculate(key));
03 }
如果两个线程(例如,t1
和t2
)同时命中此块,t1
可以执行第01行,那么t2
也可以执行01
,然后02
,然后t1
将执行02
,覆盖t2
所做的内容。操作containsKey()
和put()
分别是原子的,但应该是原子的是整个块
有时重新计算一个值并不重要,但有时确实重要,而且会中断
当涉及到并发性时,没有什么神奇之处。我的意思是,一些蹩脚的框架试图向你推销他们为你解决这个问题的想法。他们没有。即使它在99%的时间都能工作,但当你投入生产并开始获得大量流量时,它也会惊人地崩溃。或者更糟糕的是,它会默默地产生错误的结果
并发是编程中最复杂的问题之一。处理它的唯一方法就是避免它。所有这些函数式编程趋势都不是为了处理并发,而是为了完全避免并发。事实证明,最终不需要volatile。使用volatile“似乎”修复的问题实际上是一个非常微妙的对时间敏感的bug,它以一种更加优雅和恰当的方式修复了;)
所以斯布里格德斯说“简单使用”是正确的