Не делайте "ничего", пока "условие"
При просмотре кода для версии 8 на Java 8 ForkJoinPool (который имеет несколько интересных изменений от Java 7) я наткнулся на эту конструкцию (здесь):
do {} while (!blocker.isReleasable() &&
!blocker.block());
Я боюсь, почему вы напишете это так, а не просто
while (!blocker.isReleasable() &&
!blocker.block());
Это просто выбор семантики/удобочитаемости, поскольку вы можете прочитать первую конструкцию как do "nothing" while "conditions"
? Или есть некоторые дополнительные преимущества, которые мне не хватает?
Ответы
Ответ 1
Если вы читаете комментарии в верхней части файла, чуть ниже объявления класса, есть раздел, который объясняет использование этой конструкции:
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).
Ответ 2
ForkJoinPool
широко использует compareAndSwap...
из 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
Оставляя в стороне любые потенциальные преимущества, существует четкая удобочитаемость.
С while (X) ;
конечная точка с запятой не всегда очевидна с первого взгляда, вы можете быть смущены, думая, что следующий оператор или утверждения находятся внутри цикла. Например:
while (x==process(y));
if (z=x) {
// do stuff.
}
Было бы очень легко неверно понять сказанное выше, как иметь оператор if внутри цикла, и даже если бы вы его правильно прочитали, было бы легко подумать, что это ошибка программирования, а if должна находиться внутри цикла.
С do {} while(X);
, хотя сразу видно, что тела нет в цикле.
Ответ 4
Если вы прочитаете комментарий выше кода, то упоминается, что...
Если вызывающий объект не является ForkJoinTask
, этот метод поведенчески эквивалентен
while (!blocker.isReleasable())
if (blocker.block())
return;
}
Так что это просто еще одна форма для реализации выше кода в другой части...!!
В Заметки стиля упоминается, что
Существует несколько случаев необычного "do {}, в то время как (! cas...)", который является самым простым способом принудительного обновления Переменная CAS.
И если вы увидите реализацию ManagedLocker # isReleasable, она обновляет блокировку и возвращает true
, если блокировка не нужна.
Интерпретация:
Бланк, в то время как циклы используются для обеспечения прерывания до тех пор, пока какое-либо условие reset не вернется /false.
Здесь do { } while(!...)
является блокиратором/прерыванием, пока blocker.block()
не будет true
, когда blocker.isReleasable()
будет false
. Loop продолжит выполнение, а blocker
не освобождается (!blocker.isReleasable()
) и blocker
не заблокирован!! Выполнение будет вне цикла, как только blocker.block()
будет установлено значение true.
Обратите внимание, что do{ } while(...)
не обновляет переменную CAS, но гарантирует, что программа будет ждать обновления переменной (принудительно ждать обновления переменной).
Ответ 5
Вы можете легко сделать что-то вроде этого:
if(true){
//Do nothing ...
}