в какой архитектуре / ОС другой поток может видеть значения по умолчанию для нефинальных полей после вызова конструктора? - PullRequest
14 голосов
/ 09 июня 2011

Я пытаюсь воспроизвести проблему видимости памяти в случае недостаточной инициализации объекта для неоконечных полей (JLS 17.5 Конечная семантика поля , FinalFieldExample пример класса). Там, где указано «Однако f.y не является окончательным; поэтому для метода reader () не гарантируется увидеть значение 4 для него» * ​​1005 *

Я пробовал этот код:

public class ReorderingTest2 {


    public static void main(String[] args) {
        for (int i = 0; i < 2500; i++) {
            new Thread(new Reader(i)).start();
            new Thread(new Writer(i)).start();
        }
    }

    static class Reader implements Runnable {
        private String name;

        Reader(int i) {
            this.name = "reader" + i;
        }

        @Override
        public void run() {
            //System.out.println(name + " started");
            while (true) {
                FinalFieldExample.reader(name);
            }
        }
    }

    static class Writer implements Runnable {
        private String name;

        Writer(int i) {
            this.name = "writer" + i;
        }

        @Override
        public void run() {
            //System.out.println(name + " started");
            while (true) {
                FinalFieldExample.writer();
            }
        }
    }

    static class FinalFieldExample {
        int x;
        int y;
        static FinalFieldExample f;

        public FinalFieldExample() {
            x = 3;
            y = 4;
        }

        static void writer() {
            f = new FinalFieldExample();
        }

        static void reader(String name) {
            if (f != null) {
                int i = f.x;
                int j = f.y;
                if (i != 3 || j != 4) {
                    System.out.printf("reader %s sees it!%n", name);
                }
            }
        }
    }

}

Как и в предыдущей моей аналогичной теме - я пробовал на разных компьютерах (от 2 до 8 ядер) с Windows и даже на нашем сервере Solaris 32 core - я не смог воспроизвести его : fx и fy - всегда уже правильно инициализированы.

Для архитектуры Intel / x86 / x64 как Я получил ответ - у них в значительной степени есть гарантии по умолчанию , которые предотвращают переупорядочение логики конструктора. Кажется, то же самое относится и к Solaris / sparc?

Так в какой архитектуре / ОС это переупорядочение может быть воспроизведено?

Ответы [ 4 ]

1 голос
/ 11 июня 2011

Альфа.Книга Пола МакКенни Трудно ли параллельное программирование, и, если да, что вы можете с этим поделать? содержит главу, объясняющую модель памяти наиболее важных платформ.

0 голосов
/ 09 июля 2011

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

Первая часть раздела 17.4 JLS гласит:

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

Место, где я зацикливаюсь, это понимание того, что означает «как определено в остальной части этой спецификации», в отношении порядка программ.

В данном случае присвоение

f = new FinalFieldExample();

подчиняется семантике присваивания (раздел 15.26.1), к которой относится следующее. В спецификации это сбивает с толку неправильно (особенно третий шаг), я думаю, что я переформатировал его, чтобы точно отразить намерение.

[В противном случае] требуется три шага :

  1. Сначала вычисляется левый операнд для получения переменной. Если эта оценка завершается преждевременно, то выражение присваивания завершается преждевременно по той же причине; правый операнд не оценивается, и присвоение не происходит.
  2. В противном случае вычисляется правый операнд. Если эта оценка завершается преждевременно, то выражение присваивания завершается преждевременно по той же причине, и назначение не происходит.
  3. В противном случае значение правого операнда преобразуется в тип левой переменной, подвергается преобразованию набора значений (§5.1.13) в соответствующий набор стандартных значений (не расширенный -экспоненты значения), а результат преобразования сохраняется в переменной.

Это читается как спецификация однопотокового "порядка программ" для меня. Что я неправильно понимаю?

Ответ, возможно, заключается в том, что на самом деле подразумевается «тест на утку» - если один поток выполняет , как если бы все было сделано в указанном порядке, это правильная реализация. Но этот раздел написан совсем не так, как в других местах, где это делается с помощью слова , появляются , например ::

Язык программирования Java также гарантирует, что каждый операнд оператора (кроме условных операторов &&, || и? :) выглядит как для полной оценки перед выполнением любой части самой операции.

0 голосов
/ 23 июня 2011

Я предлагаю вам приобрести экземпляр "Java Concurrency in Practice" и прочитать главу 3, в которой подробно описывается, как JVM гарантирует блокировку и видимость. Ваш вопрос не имеет ничего общего с конкретной архитектурой, а все, что связано с пониманием, происходит в Java раньше.

Я думаю, что вы не можете воспроизвести проблему, потому что в конце конструктора FinalFieldExample есть край «до события», который гарантирует, что x = 3 и y = 4;

КСТАТИ. Объект FinalFieldExample немного беспорядок. Он хочет быть подходящим синглтоном, но вы так не кодировали. Отсутствие синхронизации вокруг статического «f» делает его более сложным, чем следует из соображений поведения во время выполнения этого класса. Мне кажется, это должен быть правильный синглтон с синхронизацией, защищающий доступ к статическому «f», и вы должны вызывать методы записи и чтения, такие как ...

.

FinalFieldExample.getInstance () писатель ();

Просто скажи

0 голосов
/ 11 июня 2011

Чтобы получить желаемый результат, вы можете попробовать включить интенсивную оптимизацию, поэтому запустите программу в режиме -server.

Сначала я подумал о том, чтобы сделать f volatile, но это, очевидно, испортило бы весь эксперимент.

Включите ведение журнала XML для компилятора, работающего в режиме реального времени (если вы используете JSM HotSpot), и посмотрите на сгенерированный машинный код (используя некоторый внешний отладчик или дампер памяти). Затем вы можете проверить сгенерированный код, если бы он даже позволял вам наблюдать желаемый результат.

...