Переупорядочивание jvm / тест эффекта видимости - PullRequest
5 голосов
/ 07 июня 2011

Во время написания какой-нибудь Java-статьи я пытаюсь воспроизвести переупорядочение в случае несинхронизированного построения объектов в многопоточной среде.Случай, когда тяжелый объект создается без синхронизации / volatiles / final и другие потоки получают доступ к нему сразу после вызова конструктора.Вот код, который я пробую:

public class ReorderingTest {
    static SomeObject<JPanel>[] sharedArray = new SomeObject[100];

    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            String name = "watcher" + i;
            new Thread(new Watcher(name)).start();
            System.out.printf("watcher %s started!%n", name);
        }
    }

    static class Watcher implements Runnable {
        private String name;

        Watcher(String name) {
            this.name = name;
        }

        public void run() {
            while (true) {
                int randomIndex = (int) (Math.random() * sharedArray.length);
                SomeObject<JPanel> item = sharedArray[randomIndex];
                if (item == null) {
                    //System.out.printf("sharedArray[%s]=null%n", randomIndex);
                    double r = 1 + Math.random() * 1000;
                    sharedArray[randomIndex] = new SomeObject<JPanel>(
                            new JPanel(), UUID.randomUUID().toString(), r, (float)r * 33, (long)r);
                } else {
                    //System.out.printf("sharedArray[%s]=<obj>!%n", randomIndex);
                    if (item.value == null ||
                            (item.stringField == null) ||
                            (item.doubleField == 0) ||
                            (item.floatField == 0) ||
                            (item.longField == 0)
                            ) {
                        System.err.printf("watcher %s sees default values: %s!%n", name, item);
                    } else {
                        // fully initialized! run new construction process
                        double r = 1 + Math.random() * 1000;
                        sharedArray[randomIndex] = new SomeObject<JPanel>(
                                new JPanel(), UUID.randomUUID().toString(), r, (float)r * 37, (long)r);

                    }
                }
                /*try {
                    TimeUnit.NANOSECONDS.sleep(randomIndex);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }*/
            }
        }
    }

    static class SomeObject<V> {
        V value;
        String stringField;
        double doubleField;
        float floatField;
        long longField;

        SomeObject(V value, String stringField, double doubleField, float floatField, long longField) {
            this.value = value;
            this.stringField = stringField;
            this.doubleField = doubleField;
            this.floatField = floatField;
            this.longField = longField;
        }

        @Override
        public String toString() {
            return "SomeObject{" +
                    "value=" + value == null ? "null" : "<obj>" +
                    ", stringField='" + stringField + '\'' +
                    ", doubleField=" + doubleField +
                    ", floatField=" + floatField +
                    ", longField=" + longField +
                    '}';
        }
    }
} 

-Но пока никакого эффекта нет, я пробовал на разных 2,4 и 8-ядерных ПК Intel / AMD с Windows, провел тест в течение нескольких часов - нетэффект переупорядочения - System.err.printf ("watcher% s видит ...") - не вызывается, статическая ссылка sharedArray [randomIndex] всегда содержит полностью построенные значения.

Что не так?Как воспроизвести это?

Ответы [ 4 ]

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

Вот хорошая статья, в которой должно быть показано переупорядочение на x86 (что является большим подвигом, потому что модель памяти x86 в значительной степени "безопасна"):

http://bartoszmilewski.wordpress.com/2008/11/05/who-ordered-memory-fences-on-an-x86/

Ваш пример не будет показывать переупорядочение. Компилятор не будет переупорядочивать «хранить ссылку на объект после выделения, но до построения», потому что конструктор может выдать и, следовательно, ссылку необходимо будет вернуть. Некоторые процессоры могут переупорядочить это, но не совместимые с Intel, из-за предоставленных гарантий.

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

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

В вашем случае вы ищете не переупорядочение, а объект, который, по-видимому, не был правильно инициализирован.

Ваши объекты с большой вероятностью будут использовать новую ячейку памяти каждый раз (или хотя бы ту, которая не находится в кеше). В архитектурах x86 / x64 я обнаружил, что кеш всегда будет корректным при первом его использовании.загрузить память при обновлении другим потоком.

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

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

Предположим, что происходит переупорядочение, во время записи сначала присваивается ссылка, затем заполняются поля. Ваш процесс чтения сначала читает ссылку, а затем читает поля. Таким образом, запись и чтение следуют в одном и том же порядке, для чтения почти невозможно догнать и опередить запись.

Вы можете решить проблему по

  1. пауза в конструкторе (поэтому пауза в записи)
  2. писать и читать поля в противоположном направлении

Тем не менее, изменение порядка может не произойти, и вы не сможете его наблюдать.

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

Не могли бы вы попытаться объявить sharedArray энергонезависимым? Мне кажется, что передача изменчивой ссылки на правильно сконструированный объект безопасна.

...