简体   繁体   English

基本的Java线程问题

[英]Basic Java threading issue

Lets say I'm interacting with a system that has two incrementing counters which depend on each other (these counters will never decrement): int totalFoos; 假设我正在与一个系统交互,该系统有两个相互依赖的递增计数器(这些计数器永远不会减少):int totalFoos; // barredFoos plus nonBarredFoos int barredFoos; // barredFoos加上nonBarredFoos int barredFoos;

I also have two methods: int getTotalFoos(); 我也有两个方法:int getTotalFoos(); // Basically a network call to localhost int getBarredFoos(); //基本上是对localhost int getBarredFoos()的网络调用; // Basically a network call to localhost //基本上是对localhost的网络调用

These two counters are kept and incremented by code that I don't have access to. 这两个计数器由我无法访问的代码保存和增加。 Let's assume that it increments both counters on an alternate thread but in a thread-safe manner (ie at any given point in time the two counters will be in sync). 假设它在备用线程上递增两个计数器但是以线程安全的方式递增(即在任何给定的时间点,两个计数器将同步)。

What is the best way to get an accurate count of both barredFoos and nonBarredFoos at a single point in time? 在单个时间点准确计算barredFoos和nonBarredFoos的最佳方法是什么?

The completely naive implementation: 完全天真的实现:

int totalFoos = getTotalFoos();
int barredFoos = getBarredFoos();
int nonBarredFoos = totalFoos - barredFoos;

This has the issue that the system could increment both counters in between the two method calls and then my two copies would be out of sync and barredFoos would have a value of more than it did when totalFoos was fetched. 这有一个问题,即系统可以在两个方法调用之间递增两个计数器,然后我的两个副本将不同步,并且barredFoos的值将大于获取totalFoos时的值。

Basic double-checked implementation: 基本的双重检查实施:

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
}

This should work in theory, but I'm not sure that the JVM guarantees that this is what actually happens in practice once optimization and such is taken into account. 这应该在理论上有效,但我不确定JVM是否保证这在实践中实际发生的一旦优化并考虑到这一点。 For an example of these concerns, see http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html (Link via Romain Muller). 有关这些问题的示例,请参阅http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html (通过Romain Muller链接)。

Given the methods I have and the assumption above that the counters are in fact updated together, is there a way I can guarantee that my copies of the two counts are in sync? 鉴于我拥有的方法和上面的假设,计数器实际上是一起更新的,有没有办法可以保证我的两个计数副本是同步的?

Yes, I believe your implementation will be sufficient; 是的,我相信你的实施就足够了; the real work is making sure that the values that are returned by getTotalFoos and getBarredFoos are indeed synchronized and always returning the latest values. 真正的工作是确保getTotalFoosgetBarredFoos返回的值确实已同步并始终返回最新值。 However, as you've said, this is already the case. 但是,正如你所说,情况已经如此。

Of course, one thing you could run in to with this code is an endless loop; 当然,使用此代码可以运行的一件事是无限循环; you would want to be sure that the two values being changed in such a short time would be a very exceptional situation, and even then I think that it would definitely be wise to build in a safety (ie maximum number of iterations) to avoid getting into an endless loop. 你想要确保在如此短的时间内改变这两个值将是一个非常特殊的情况,即便如此我认为建立一个安全(即最大迭代次数)以避免获得肯定是明智的陷入无尽的循环。 If the value coming out of those counter is in code that you don't have access to, you don't want to be totally relying on the fact that things will never go awry at the other end. 如果来自这些计数器的值是您无法访问的代码,那么您不希望完全依赖于事情永远不会在另一端出错的事实。

To guarantee read consitency across threads - and prevent code execution re-ordering, especially on muli-core machines, you need to synchronize all read and write access to those variables. 为了保证跨线程的读取性能 - 并防止代码执行重新排序,尤其是在多核机器上,您需要同步对这些变量的所有读写访问。 In addition, to ensure that on a single thread you see the most up to date values of all variables being used in the current computation you need to synchronise on read access. 此外,为了确保在单个线程上您可以看到当前计算中使用的所有变量的最新值,您需要在读取访问时进行同步。

Update: I missed the bit about the calls to get the values of both variables being separate calls over the network - which renders this the double-checked locking problem (so without an api method available to you that returns both values at once you cann't absolutely guarantee consistency of both variables at any point in time). 更新:我错过了关于调用以获取两个变量的值是通过网络进行单独调用的调用 - 这使得双重检查锁定问题(因此没有可用的api方法,可以立即返回两个值)绝对保证在任何时间点两个变量的一致性)。

See Brian Goetz's article on Java memory model . 请参阅Brian Goetz关于Java内存模型的文章。

可能无法可靠地执行您想要的操作,除非您正在与之交互的系统具有一种方法,使您能够一次性(以原子方式)检索这两个值。

I was going to mention AtomicInteger as well, but that won't work, because 我也要提到AtomicInteger,但这不起作用,因为

1) you've got TWO integers, not just one. 1)你有两个整数,而不只是一个。 AtomicIntegers won't help you. AtomicIntegers不会帮助你。 2) He doesn't have access to the underlying code. 2)他无权访问底层代码。

My question is, even if you can't modify the underlying code, can you control when it's executed? 我的问题是,即使你不能修改底层代码,你能控制它何时被执行? You could put synchronization blocks around any functions that modify those counters. 您可以在修改这些计数器的任何函数周围放置同步块。 That might not be pleasant, (it could be slower then your loop) but that would arguably be the 'correct' way to do it. 这可能不太令人愉快,(它可能比你的循环慢),但这可能是“正确”的做法。

If you can't even control the internal threads, then I guess your loop would work. 如果你甚至无法控制内部线程,那么我猜你的循环会起作用。

And finally, if you ever do get control of the code, the best thing would be to have one synchronized function that blocks access to both integers as it runs, and returns the two of them in an int[]. 最后,如果您确实获得了对代码的控制权,那么最好的方法是让一个同步函数在运行时阻止对两个整数的访问,并在int []中返回它们中的两个。

Given that there's no way to access whatever locking mechanism is maintaining the invariant "totalFoos = barredFoos + nonBarredFoos", there's no way to ensure that the values you retrieve are consistent with that invariant. 鉴于没有办法访问任何锁定机制维持不变的“totalFoos = barredFoos + nonBarredFoos”,因此无法确保您检索的值与该不变量一致。 Sorry. 抱歉。

The method that contains the code 包含代码的方法

while (true) {    
 int totalFoos = getTotalFoos();    
 int barredFoos = getBarredFoos();    
 if (totalFoos == getTotalFoos()) {        
  int nonBarredFoos = totalFoos - barredFoos;        
  break;    
 } 

} }

Should be synchronized 应该同步

private synchronized void getFoos()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM