пример нестабильного jls

Если следующий код не вызывает AssertionError в Windows 7 x86 jdk 7 (с включенным -ea) на Пример JLS для volatile?

public class TestVolatile {
static volatile int i = 0;
static volatile int j = 0;

static void one() {
    i++;
    j++;
    assert (i>=j);
//:"one: i=" + i + " j=" + j;
}
static void two() {
    //System.out.println("i=" + i + " j=" + j);
    assert (i<=j);
    /*
    System.out.print("<i=" + i);
    for (int k = 0; k < 1000000; k++);
    System.out.println(", j=" + j+">");
    */
}
public static final int NUM_WORKERS =  4;

public static void main (String [] args) {
    final Worker [] workers = new Worker[NUM_WORKERS];
    final Thread [] workerThreads = new Thread[NUM_WORKERS];

    for (int i = 0; i < NUM_WORKERS; i++) {
        Worker w = new Worker(i);
        workers[i] = w;
        workerThreads[i] = new Thread(w,"workerThread_"+i);
    }

    for (int i = 0; i < NUM_WORKERS; i++) {
        workerThreads[i].start();
    }

}


}

final class Worker implements Runnable {
final int id;
volatile boolean notDone = true;


public Worker(int tid){
    id = tid;
}

@Override
public void run() {
    //System.out.println("worker start:" + id);
    try {
        while (notDone) {
            if (id  <  TestVolatile.NUM_WORKERS - 1) {
                TestVolatile.one();
            } else {
                TestVolatile.two();
            }
        }
    } catch (Exception e) {
        // TODO: handle exception
        e.printStackTrace();
    }
}
};

person porkchop    schedule 13.01.2013    source источник
comment
Я получаю сообщение об ошибке почти сразу.   -  person assylias    schedule 13.01.2013
comment
где вы ожидаете ошибки и почему? объяснить   -  person exexzian    schedule 13.01.2013
comment
Я тоже получаю сообщение об ошибке без промедления, и это достоверно.   -  person Marko Topolnik    schedule 13.01.2013
comment
@MarkoTopolnik Если честно, я нахожу формулировку внизу пример 8.3.1.4-1 немного вводит в заблуждение: Следовательно, общее значение для j никогда не бывает больше, чем для i, потому что каждое обновление i должно отражаться в общем значение для i до того, как произойдет обновление до j.   -  person assylias    schedule 13.01.2013
comment
Его нужно читать в контексте, где есть только один поток записи.   -  person assylias    schedule 14.01.2013
comment
Да, здесь у нас N писателей и один читатель. Все ставки сделаны.   -  person Marko Topolnik    schedule 14.01.2013
comment
@assylias даже для одного автора это утверждение неверно, что подтверждается даже следующим предложением: «Однако возможно, что при любом данном вызове метода два может наблюдаться значение j, которое намного больше, чем значение соблюдается для i,… ». Таким образом, j может быть больше i, и утверждение обратного в предыдущем предложении… в лучшем случае вводит в заблуждение. Есть новые вопросы и ответы по этому поводу, и имхо, этот пример должен гореть в аду ...   -  person Holger    schedule 24.05.2019


Ответы (1)


У вас работает более 1 потока one. i и j изменчивы, поэтому изменения будут видны, однако i++ и j++ не являются атомарными операциями, и очень вероятно, что один из счетчиков не будет должным образом увеличен на каком-то этапе:

Скажем, i = 5, например, чтобы показать допустимое чередование потоков, которое может вызвать ошибку AssertionError:

  • Тема 1: прочтите i => 5
  • Тема 2: прочтите i => 5
  • Поток 1: temp = i + 1 => 6
  • Поток 2: temp = i + 1 => 6
  • Поток 1: напишите i = temp => 6
  • Поток 2: напишите i = temp => 6
  • Поток 1: читать j и увеличивать => j = 6
  • Поток 2: читать j и увеличивать => j = 7

И i и j не синхронизированы, и ваше утверждение в one завершится ошибкой.

Но все же возможно, что запуск программы не приведет к ошибке утверждения, несмотря на наличие этой гонки данных: JLS не гарантирует, что ваша программа будет работать нормально, но и не говорит, что это не так.

РЕДАКТИРОВАТЬ: один из парней, написавших раздел JLS «Потоки и блокировки», на самом деле имеет опубликовать в своем блоге очень похожий вопрос. В комментариях даже упоминается раздел JLS, на который вы ссылаетесь в своем вопросе: в примере JLS есть только один поток записи.

person assylias    schedule 13.01.2013
comment
+1 volatile не означает, что все операции над ним атомарны, только чтение или запись, но не то и другое одновременно. Я считаю, что приведенный пример работает только там, где это один поток, выполняющий обновления. - person Peter Lawrey; 13.01.2013
comment
Я не уверен, что пример пройдет, даже если обновления будет выполнять только один поток. но я полностью согласен, что это элементарная проблема в тестовом коде. спасибо, ребята, это место похоже на тренировку в реальном времени для JLS. - person porkchop; 14.01.2013
comment
еще один пример доказательства того, что изменчивость не гарантирует атомарность: brooker.co.za /blog/2012/11/13/increment.html - person porkchop; 14.01.2013