Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/354.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
基本Java线程问题_Java_Optimization_Java Me_Thread Safety - Fatal编程技术网

基本Java线程问题

基本Java线程问题,java,optimization,java-me,thread-safety,Java,Optimization,Java Me,Thread Safety,假设我正在与一个系统交互,该系统有两个相互依赖的递增计数器(这些计数器永远不会递减): int totalFoos;//巴雷德福加上非巴雷德福 内巴雷德福 我还有两种方法: int getTotalFoos();//基本上是对本地主机的网络调用 int getBarredFoos();//基本上是对本地主机的网络调用 这两个计数器由我无权访问的代码保留和递增。让我们假设它以线程安全的方式增加备用线程上的两个计数器(即,在任何给定的时间点,两个计数器将同步) 在一个时间点获得准确的barredFo

假设我正在与一个系统交互,该系统有两个相互依赖的递增计数器(这些计数器永远不会递减): int totalFoos;//巴雷德福加上非巴雷德福 内巴雷德福

我还有两种方法: int getTotalFoos();//基本上是对本地主机的网络调用 int getBarredFoos();//基本上是对本地主机的网络调用

这两个计数器由我无权访问的代码保留和递增。让我们假设它以线程安全的方式增加备用线程上的两个计数器(即,在任何给定的时间点,两个计数器将同步)

在一个时间点获得准确的barredFoos和nonBarredFoos计数的最佳方法是什么

完全幼稚的实现:

int totalFoos = getTotalFoos();
int barredFoos = getBarredFoos();
int nonBarredFoos = totalFoos - barredFoos;
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
}
这有一个问题,即系统可能会在两个方法调用之间增加两个计数器,然后我的两个副本将不同步,
barredFoos
的值将大于提取
totalFoos
时的值

基本的双重检查实现:

int totalFoos = getTotalFoos();
int barredFoos = getBarredFoos();
int nonBarredFoos = totalFoos - barredFoos;
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是否能保证,一旦考虑到优化等因素,在实践中就会发生这种情况。有关这些问题的示例,请参阅(通过Romain Muller链接)


考虑到我的方法和上述计数器实际上一起更新的假设,我有没有办法保证两个计数的副本同步?

以保证线程间的读取一致性,并防止代码执行重新排序,特别是在多核机器上,您需要同步对这些变量的所有读写访问。此外,为了确保在单个线程上看到当前计算中使用的所有变量的最新值,您需要在读取访问时进行同步

更新:我错过了一点关于调用的信息,这两个变量的值都是通过网络进行的单独调用,这就导致了双重检查锁定问题(因此,如果没有一个可以同时返回两个值的api方法,就无法绝对保证两个变量在任何时间点的一致性)


请参阅Brian Goetz的文章。

是的,我相信您的实现就足够了;真正的工作是确保
getTotalFoos
getBarredFoos
返回的值确实是同步的,并且总是返回最新的值。但是,正如您所说的,这已经是事实了


当然,使用此代码可以遇到的一个问题是无休止的循环;您需要确保在如此短的时间内更改的两个值将是一个非常特殊的情况,即使如此,我认为构建安全性(即最大迭代次数)肯定是明智的避免陷入无休止的循环。如果从这些计数器中输出的值是在您无权访问的代码中,则您不希望完全依赖于这样一个事实,即在另一端,事情永远不会出错。

除非与您交互的系统有一种方法使您能够一次检索两个值(以原子的方式)。

我还想提到AtomicInteger,但这不起作用,因为

1) 你有两个整数,而不是一个。原子整数帮不了你。 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()

哪些变量?这些字段是在一个他无法访问的系统中声明的,而将局部变量变为易失性是毫无意义的,因为只有一个线程可以访问它们。我认为这些变量的值没有任何问题,因为它们只能在一个线程内访问。我还必须在本质上相同的时间点得到两种方法的结果(确切地说,在什么时间点并不重要,只是两个变量的结果是相同的)
totalFoo
s在我访问
barredFoo
s之前不能更改,否则查找
nonBarredFoo
s的减法将被抛出。嗯,是的,应该更仔细地阅读这个问题-错过了关于网络呼叫的部分。这是罪过。你不应该依靠双重检查来在Java中正常工作:@RomainMuller谢谢你的链接。这正是我在关注上面的后一个示例时所指的那种事情。我第一次看到这篇文章时就把它完全添加到书签中。。。Java中有很多非常常见的错误,几乎每个人都在不知道黑暗中潜伏着邪恶的情况下犯下这些错误……事实上,双重检查锁定在Java中不起作用;然而,这不是一个问题。这是只在单个线程中运行的代码(除非我弄错了)。它是多线程的,因为变量的值在网络上的另一台主机上发生了更改-因此它确实适用(我认为!)+1。为了不必玩retry hokey pokey,您需要同步metho