2013-05-08 43 views
8

雖然與做,而在什麼情況下可以做到 - 雖然比以前更有效率?

雖然和do-while在功能上等同當塊是空,但同時似乎更自然:一段時間的

do {} while (keepLooping()); 
while (keepLooping()) {} 

一個典型使用案例/ do-while使用空白塊是強制使用compareAndSet(CAS)更新原子對象。例如下面的代碼將在一個線程安全的方式增加a

int i; 
AtomicInteger a = new AtomicInteger(); 
while (!a.compareAndSet(i = a.get(), i + 1)) {} 

語境

java.util.concurrent中的幾個部分使用do {} while (...)成語CAS操作和ForkJoinPool javadoc的解釋:

有幾次發生的異常do {} while (!cas...)這是強制更新CAS'ed變量的最簡單方法。

因爲他們承認這是不尋常的,我想他們的意思最好而不是簡單

問題

是否在某些do {} while (!cas)可以比while (!cas) {}出於什麼原因,更有效的情況呢?

+0

也許這只是歷史的偏好。無論如何,爲什麼要使用'{}'--- while(condition);'是我寫的。 – 2013-05-08 10:24:30

+1

@MarkoTopolnik在';'上同意。我發現了[這段視頻](Doug Lea在大約57分鐘左右的時候說道)(http://emergingtech.chariotsolutions.com/2013/04/phillyete-screencast-7-doug-lea-engineering-concurrent-library-components/) :「*不要使用while,使用do-while,因爲safepoints *」,並且幻燈片提到「較小的競賽窗口」。我想他提到了GC安全點,儘管我沒有看到它在這裏有什麼不同。 – assylias 2013-05-08 10:41:15

+1

Doug對此很籠統:)我能解釋它的最好方式不是關於do-while和while,而是關於使用循環體進行分配與填充所有條件。 – 2013-05-08 10:56:23

回答

0

可能有一些情況,期望和更新的計算複雜,以便在您調用compareAndSet的同一行中可讀。 然後,你可以讓它裏面做的更加易讀:

do { 
    int expect = a.get(); 
    int update = expect + 1; 
} while (!a.compareAndSet(expect, update)); 
+0

該問題假定爲空白區塊,以便「儘可能」與「嚴格等同」。 – assylias 2013-05-08 10:14:06

+0

啊哈。 Maby更喜歡它 – 2013-05-08 10:44:10

+0

請參閱這個實例:http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/7-b147/java/util/concurrent/ForkJoinPool。 java #ForkJoinPool.postBlock%28%29 - 我不認爲這是問題。 – assylias 2013-05-08 10:46:41

0

這不是效率的問題。如果沒有{while(),有些情況下無法解決。看看java.util.Random.next(int bits)。如果你試圖用while(){}來做同樣的事情,你會得到一個重複的代碼,因爲循環體必須在條件前執行一次。

我已經問過一個非常類似的問題:compiling loops in Java

此代碼:

public class Test { 

    static int i = 0; 

    public static void main(String[] args) { 
     method1(); 
     method2(); 
    } 

    public static void method2() { 
     do{}while(++i < 5); 
    } 

    public static void method1() { 
     while(++i < 5); 
    } 
} 

被編譯成:

public static void method2(); 
    Code: 
    0: getstatic  #4; //Field i:I 
    3: iconst_1 
    4: iadd 
    5: dup 
    6: putstatic  #4; //Field i:I 
    9: iconst_5 
    10: if_icmplt  0 
    13: return 

public static void method1(); 
    Code: 
    0: getstatic  #4; //Field i:I 
    3: iconst_1 
    4: iadd 
    5: dup 
    6: putstatic  #4; //Field i:I 
    9: iconst_5 
    10: if_icmpge  16 
    13: goto 0 
    16: return 

你可能會注意到方法1在第13行額外的指令()。但正如我的問題所回答的那樣,當JIT編譯成機器指令時,這沒有任何區別。非常難以捉摸的性能改進。任何證明它必須使用PrintAssembly鍵運行的方法。理論上方法2更快,但實際上它們應該是平等的。

+2

你沒有仔細閱讀過這個問題。根本沒有循環體,效率的問題是非常低的,關於最終的本地代碼排序。 – 2013-05-08 11:02:04

+0

我剛纔看了一下裝配,沒有發現任何東西。 – assylias 2013-05-08 14:31:33

+0

你是什麼意思?這一切都一樣嗎? – Mikhail 2013-05-08 17:46:59

2

因此,'儘管'意味着它會在while循環中運行一次代碼。然後,如果條件爲真,它只運行while循環內的代碼。

簡單的演示

boolean condition = false; 

do{ 
    System.out.Println("this text displayed"); 
}while(condition == true); 

輸出 「這一文本顯示」

普通

while(condition == true){ 
System.out.Println("this text displayed"); 
} 

輸出 「」

  • *顯示,由於條件是假無輸出。

爲什麼或在哪裏你會使用做的時候,我不能滿足需要,所以我不能幫你在那裏。這只是一個識別問題/需求的問題,並且使用你知道的解決問題的方式。類似於樂高 - 機械類不是「阻止」。

相關問題