[英]Basic Java threading issue
假设我正在与一个系统交互,该系统有两个相互依赖的递增计数器(这些计数器永远不会减少):int totalFoos; // barredFoos加上nonBarredFoos int barredFoos;
我也有两个方法:int getTotalFoos(); //基本上是对localhost int getBarredFoos()的网络调用; //基本上是对localhost的网络调用
这两个计数器由我无法访问的代码保存和增加。 假设它在备用线程上递增两个计数器但是以线程安全的方式递增(即在任何给定的时间点,两个计数器将同步)。
在单个时间点准确计算barredFoos和nonBarredFoos的最佳方法是什么?
完全天真的实现:
int totalFoos = getTotalFoos();
int barredFoos = getBarredFoos();
int nonBarredFoos = totalFoos - barredFoos;
这有一个问题,即系统可以在两个方法调用之间递增两个计数器,然后我的两个副本将不同步,并且barredFoos
的值将大于获取totalFoos
时的值。
基本的双重检查实施:
while (true) {
int totalFoos = getTotalFoos();
int barredFoos = getBarredFoos();
if (totalFoos == getTotalFoos()) {
// totalFoos did not change during fetch of barredFoos, so barredFoos should be accurate.
int nonBarredFoos = totalFoos - barredFoos;
break;
}
// totalFoos changed during fetch of barredFoos, try again
}
这应该在理论上有效,但我不确定JVM是否保证这在实践中实际发生的一旦优化并考虑到这一点。 有关这些问题的示例,请参阅http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html (通过Romain Muller链接)。
鉴于我拥有的方法和上面的假设,计数器实际上是一起更新的,有没有办法可以保证我的两个计数副本是同步的?
是的,我相信你的实施就足够了; 真正的工作是确保getTotalFoos
和getBarredFoos
返回的值确实已同步并始终返回最新值。 但是,正如你所说,情况已经如此。
当然,使用此代码可以运行的一件事是无限循环; 你想要确保在如此短的时间内改变这两个值将是一个非常特殊的情况,即便如此我认为建立一个安全(即最大迭代次数)以避免获得肯定是明智的陷入无尽的循环。 如果来自这些计数器的值是您无法访问的代码,那么您不希望完全依赖于事情永远不会在另一端出错的事实。
为了保证跨线程的读取性能 - 并防止代码执行重新排序,尤其是在多核机器上,您需要同步对这些变量的所有读写访问。 此外,为了确保在单个线程上您可以看到当前计算中使用的所有变量的最新值,您需要在读取访问时进行同步。
更新:我错过了关于调用以获取两个变量的值是通过网络进行单独调用的调用 - 这使得双重检查锁定问题(因此没有可用的api方法,可以立即返回两个值)绝对保证在任何时间点两个变量的一致性)。
请参阅Brian Goetz关于Java内存模型的文章。
您可能无法可靠地执行您想要的操作,除非您正在与之交互的系统具有一种方法,使您能够一次性(以原子方式)检索这两个值。
我也要提到AtomicInteger,但这不起作用,因为
1)你有两个整数,而不只是一个。 AtomicIntegers不会帮助你。 2)他无权访问底层代码。
我的问题是,即使你不能修改底层代码,你能控制它何时被执行? 您可以在修改这些计数器的任何函数周围放置同步块。 这可能不太令人愉快,(它可能比你的循环慢),但这可能是“正确”的做法。
如果你甚至无法控制内部线程,那么我猜你的循环会起作用。
最后,如果您确实获得了对代码的控制权,那么最好的方法是让一个同步函数在运行时阻止对两个整数的访问,并在int []中返回它们中的两个。
鉴于没有办法访问任何锁定机制维持不变的“totalFoos = barredFoos + nonBarredFoos”,因此无法确保您检索的值与该不变量一致。 抱歉。
包含代码的方法
while (true) {
int totalFoos = getTotalFoos();
int barredFoos = getBarredFoos();
if (totalFoos == getTotalFoos()) {
int nonBarredFoos = totalFoos - barredFoos;
break;
}
}
应该同步
private synchronized void getFoos()
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.