При каких условиях записи в энергонезависимые переменные будут невидимы для других потоков? Могу ли я форсировать такие условия в экспериментальных целях?

Недавно я много читал здесь, в SO, и в других местах об управлении многопоточной памятью, в частности, об использовании ключевого слова volatile. Я начинаю чувствовать себя достаточно уверенно в концепции, однако, чтобы в полной мере оценить эффект, который она оказывает, я хотел бы попытаться провести несколько экспериментов, которые ее иллюстрируют.

Вот моя настройка: у меня есть поток производителя (он считывает аудиоданные с микрофона, связанные с моим предыдущий вопрос, но фактические данные не имеют значения), который передает данные как byte[] в отдельный поток-потребитель. Способ, которым данные распределяются между потоками, является основной переменной в моем эксперименте: я пробовал ArrayBlockingQueue; Я попробовал общую volatile byte[] ссылку (с array = array ссылкой на себя, как рекомендовано в эта запись в блоге); и я также пробовал обычный энергонезависимый byte[] без ссылки на себя. Оба потока также записывают данные на диск по мере их выполнения.

Я надеялся обнаружить, что после запуска в течение некоторого времени энергонезависимая версия byte[] будет иметь несоответствия между данными, которые производитель пытался передать, и данными, которые читал потребитель из-за того, что некоторые записи памяти не видны в памяти. время, в то время как две другие версии будут иметь точно такие же данные, регистрируемые каждым потоком, из-за мер предосторожности, принятых для обеспечения публикации записей памяти. Однако, как бы то ни было, я нахожу 100% точность, какой бы метод я ни использовал.

Я уже могу придумать несколько вариантов того, почему это произошло, но мой главный вопрос заключается в следующем: при каких условиях выполняется запись в энергонезависимую переменную, невидимую для другого потока, что, насколько я понимаю, весь смысл volatile? И могу ли я форсировать эти условия в экспериментальных целях?

Мои мысли пока такие:

  • Может быть, два потока работают на одном ядре и используют один и тот же кеш, поэтому записи в память видны сразу?
  • Может быть, загрузка процессора является фактором? Возможно, мне нужно много потоков, выполняющих разные задачи, прежде чем я увижу какую-либо проблему?
  • Может быть, мне нужно подождать дольше: возможно, такие проблемы очень редки?

Может ли кто-нибудь предложить, как я мог спланировать такой эксперимент, или объяснить, почему моя идея ошибочна?

Большое спасибо.


person devrobf    schedule 28.08.2013    source источник
comment
Дело в том, что при многих условиях не гарантируется синхронизация энергонезависимых переменных, а не в любых определенных условиях, когда они гарантированно не синхронизируются. Наиболее вероятный способ запустить его — зациклиться, но это не гарантировано.   -  person kiheru    schedule 28.08.2013
comment
Да, я понимаю, что это не гарантируется, и это делает это очень проблематичным, так что, возможно, просто невозможно провести такой эксперимент. Тем не менее, я хотел узнать, есть ли у кого-нибудь какие-либо идеи, поэтому спасибо за ваш комментарий.   -  person devrobf    schedule 28.08.2013
comment
на процессорах Intel записи упорядочены, поэтому трудно заметить какое-либо несоответствие. компилятор/среда выполнения могут изменить порядок инструкций по оптимизации; если у нас будет достаточно понимания того, как это работает, мы можем создать ситуацию, при которой произойдет переупорядочивание.   -  person ZhongYu    schedule 28.08.2013
comment
Процессоры x86 и x64 используют MESI для обеспечения согласованности кешей, поэтому я боюсь, что вы никогда не увидите устаревшие значения. Основная цель volatile на однопроцессорных машинах — предотвратить переупорядочивание команд. Если вы тщательно обдумаете это, вы можете заставить компилятор что-то переупорядочить, чтобы вы могли наблюдать записи, которые происходят в порядке, отличном от того, который вы написали.   -  person Giulio Franco    schedule 28.08.2013
comment
@ zhong.j.yu Лучше всего помочь JIT поднять переменную - см. Мой пример.   -  person assylias    schedule 29.08.2013


Ответы (1)


Вы не сможете легко наблюдать эффекты отсутствия барьеров в вашем коде на x86, потому что у него довольно сильная модель памяти. Но это не значит, что один и тот же код не сломается на другой архитектуре. На x86 вообще нужно поиграться с JIT-компилятором и помочь ему сделать оптимизацию, которая не позволила бы с volatile переменной, например подъем переменной.

Приведенный ниже код на моей машине с сервером точки доступа 7u25 никогда не завершается, если переменная является энергонезависимой, но останавливается сразу же, если это так. Возможно, вам придется изменить задержку сна в зависимости от вашей машины.

public class Test {

    static /* volatile */ boolean done = false;

    public static void main(String[] args) throws Exception {
        Runnable waiter = new Runnable() {
            @Override public void run() {
                while(!done);
                System.out.println("Exited loop");
            }
        };
        new Thread(waiter).start();
        Thread.sleep(100); //wait for JIT compilation
        done = true;
        System.out.println("done is true");
    }
}
person assylias    schedule 29.08.2013
comment
Это очень умная идея, работает и для меня. Я думаю, это отличная иллюстрация, спасибо! - person devrobf; 29.08.2013
comment
@gstackoverflow не уверен, почему вы хотите удалить аннотацию Override - рекомендуется использовать ее там, где это применимо. - person assylias; 24.04.2014
comment
@assylias Я пытался скомпилировать этот код и вижу ошибку компиляции. - person gstackoverflow; 24.04.2014
comment
Мне жаль. возможно это баг моей IDE - person gstackoverflow; 24.04.2014
comment
@gstackoverflow, на каком уровне исходного языка установлен ваш компилятор? Реализация метода интерфейса является допустимым использованием @Override в java 6 или более поздней версии, но не разрешена в java 5 (только истинные переопределения метода суперкласса могут принимать аннотацию в 5). - person Ian Roberts; 24.04.2014
comment
@ Ян Робертс Да. ты прав. только в 5-й версии нельзя - можно в 6+ версиях - person gstackoverflow; 24.04.2014