做“条件”时“不做”


70

浏览ForkJoinPool的Java 8版本的代码(它与Java 7相比有一些有趣的变化),我遇到了这个结构(在这里):

do {} while (!blocker.isReleasable() &&
             !blocker.block());

我在为您为什么要像这样写而苦苦挣扎

while (!blocker.isReleasable() &&
       !blocker.block());

因为您可以将第一个构造读取为,这仅仅是语义/可读性选择do "nothing" while "conditions"吗?还是我还缺少其他好处?

Answers:


54

如果您在类声明下面的文件顶部阅读注释,则有一节解释了此构造的用法:

Style notes

===========

[...]

There are several occurrences of the unusual "do {} while
(!cas...)"  which is the simplest way to force an update of a
CAS'ed variable. There are also other coding oddities (including
several unnecessary-looking hoisted null checks) that help
some methods perform reasonably even when interpreted (not
compiled).

25
由于我自己需要提醒:CAS是en.wikipedia.org/wiki/Compare-and-swap
Erik Vesteraas 2014年

14
您可能已经解释了该构造如何帮助“强制更新CAS'ed变量”。
Honza Zidek 2014年

7
但是,为什么它会引起与while版本不同的CAS'ed变量的更新?
本杰明·格伦鲍姆

4
它没有说{} while(condition)vs. while(condition)是否做;有所不同。如果有所作为,那么这将是(a)一个糟糕的错误评论,(b)我认为这是一个在语句本身没有评论的错误,并且(c)有所作为的事实是语言。
gnasher729 2014年

3
这听起来很神奇,范围更改do {} while()避免了所while()使用的JIT优化。while语句在语义上是等效的,因此区别必须在JVM的实现上。
Michael Shopsin 2014年

45

ForkJoinPool正如其他答案所提到的,此注释在标题“样式注释”下对此进行了解释,从而充分利用了罐头中compareAndSwap...from的sun.misc.Unsafe大部分出现的情况:do {} while (...)ForkJoinPool

* There are several occurrences of the unusual "do {} while
* (!cas...)"  which is the simplest way to force an update of a
* CAS'ed variable. 

使用写while带有空主体的-loop的选择do {} while (condition)似乎是一种风格上的选择。在中HashMap,这可能更清楚了,它恰巧在Java 8中进行了更新。

在Java 7中,HashMap您可以找到以下代码:

while (index < t.length && (next = t[index++]) == null)
    ;

尽管围绕它的许多代码也已更改,但很明显,Java 8中的替换是:

do {} while (index < t.length && (next = t[index++]) == null);

第一个版本的缺点是,如果唯一的分号碰巧被删除,则它会根据下一行来更改程序的含义。

如下所示,while (...) {}和生成的字节码do {} while (...);略有不同,但在运行时不会以任何方式影响任何内容。

Java代码:

class WhileTest {
    boolean condition;

    void waitWhile() {
        while(!condition);
    }

    void waitDoWhile() {
        do {} while(!condition);
    }
}

生成的代码:

class WhileTest {
  boolean condition;

  WhileTest();
    Code:
       0: aload_0       
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return        

  void waitWhile();
    Code:
       0: aload_0       
       1: getfield      #2                  // Field condition:Z
       4: ifne          10
       7: goto          0
      10: return        

  void waitDoWhile();
    Code:
       0: aload_0       
       1: getfield      #2                  // Field condition:Z
       4: ifeq          0
       7: return        
}

3
来自一个重复的问题:与当前接受的答案相比,此恕我直言是一个更好,更令人信服的答案。
Marco13 '13

8

除了潜在的性能优势外,还有明显的可读性优势。

随着while (X) ;尾随分号是不是第一眼总是很明显,你可能会误以为下面的语句或语句是在循环中。例如:

while (x==process(y));
if (z=x) {
    // do stuff.
}

将上述内容误认为是在循环中包含if语句是很容易的,即使您正确地阅读了它,也很容易认为这是一个编程错误,而if应该在循环中。

随着do {} while(X);虽然它立即一目了然,没有身体的循环。


为什么不简单地写while (x==process(y)) {}呢?我认为这几乎与do ... while()解决方案一样清楚。
exilit 2014年

@exilit会有所帮助,但由于该do {} while结构在方括号之间具有关键字,因此可以使分隔符更清洁。即使在这个简单的示例中,我们彼此之间也已经有4个括号了)){}-您可以轻易地看到,如果数字变得越来越复杂,该数字可能会增加...因此,不理会{}所有其他字符会更容易。
2014年

4

如果您将阅读代码上方的注释,则会提到...

如果调用方不是ForkJoinTask,则此方法在行为上等效于

while (!blocker.isReleasable())
   if (blocker.block())
      return;
}

因此,这只是实现上述代码的另一种形式……!

在“样式注释”中提到,

多次出现不寻常的“ do {} while(!cas ...)”,这是强制更新CAS变量的最简单方法。

并且,如果您将看到ManagedLocker#isReleasable的实现,它将更新锁,true如果不需要阻塞,则返回。

释义:

空白循环将用于提供中断,直到某些条件重置为true / false。

在这里,do { } while(!...)是一个拦截器/中断,直到blocker.block()trueblocker.isReleasable()false。循环将在blocker不可释放(!blocker.isReleasable()blocker且未被阻止的情况下继续执行!执行将尽快退出循环blocker.block()设置为true。

请注意,do{ } while(...)它不会更新CAS变量,但可以保证程序将等待直到变量被更新(强制等待直到变量被更新)。


是的,但是两种情况都使用do {} while(condition)构造。因此,评论确实只是写同一件事的第三种方式。
Erik Vesteraas 2014年

@ErikVesteraas更新了答案,当您评论时它仍在进行中:D
不是错误

那么样式注释中的注释是否暗示while(!cas...)不会强制进行更新?如字节码所示,是否存在实际的技术差异?我开始怀疑是否查看isReleasable是否会分散注意力,因为在很多地方,它用于实际的CAS变量,例如:do {} while (!U.compareAndSwapLong(...));
Erik Vesteraas 2014年

@ErikVesteraas我不认为isReleasable()这会分散注意力,请参见解释编辑。
不是错误

2
是的,我同意您所写的内容,但未解决是否while(!cas...);可以对CompareAndSwap变量强制进行更新,或者是否do {} while (!cas...);有必要(即是否存在实际的技术差异)。
Erik Vesteraas 2014年

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.