Демонстрация проблем с неправильно опубликованными объектами - PullRequest
3 голосов
/ 20 декабря 2011

JCIP предупреждает нас о неправильно опубликованных объектах (см. здесь ).Если объект является изменяемым, JVM может решить опубликовать объект до завершения его инициализации.

Таким образом, код

class Holder {public int h = 0;public Holder(int _h) {h = _h;}}
a = new Holder(10);

может стать эффективным

a = new Holder();
// inlined constructor
a.h = 10;

затем, когда другой поток обращается к a.h, предполагая, что он никогда не может быть 0, он может завершиться ошибкой

// Thread #1 executing
a = new Holder();
// Thread #2 preempts
if (a != null) System.out.println("result: "+(100/a.h)); // oops...

Я пытался продемонстрировать эту проблему.Но ни один код, который я написал, не продемонстрировал этого.То, что я сделал, было:

static class Holder {
    int h = 0;
    int dummy = 0;
    public Holder(int h) {
        // if construction takes time, maybe I have better chance creating the bug...
        for (long i=0;i<1000*1000;i++) {
            dummy += i*(-1+(i%2*2));
        }
        this.h = h;
    }
    public int verifyNonZero() {
        return 1/h;
    }
}

Затем я запустил 1000 потоков, публикующих new Holder() в статическую переменную holder, и другие 1000 потоков, работающих holder.verifyNonZero().См. полный текст .

Это не очень помогло с Java 1.6, даже с -server.

Ответы [ 2 ]

3 голосов
/ 20 декабря 2011

Тот факт, что безопасность не гарантируется, не означает, что в вашей среде что-то пойдет не так.

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

Также обратите внимание, что выполнение не по порядку может быть вызвано не только JVM, но и процессором.Однако модель памяти процессоров x86 довольно консервативна ( Кто заказал заборы памяти на платформе x86? ), так что это поведение не может быть вызвано также часто используемыми процессорами.

Итак, ядумаю, что вряд ли вы сможете решить эту проблему в типичной современной среде.

0 голосов
/ 20 декабря 2011

Как проходит тест? [РЕДАКТИРОВАТЬ: о, вы связаны с кодом!]

JMM гарантирует, что в данном потоке вы не сможете видеть вещи не в порядке - то есть, если тот же поток создает экземпляр Holder и проверяет его, он никогда не увидит неинициализированное состояние.

Итак, вам нужно создать экземпляр Держателя в одной ветке, а затем увидеть его в другой. Как вы делитесь этим экземпляром? Обычные методы (изменяемая статическая, синхронизированная или поточно-ориентированная коллекция и т. Д.) Устанавливают связь между событием до того, как Holder был опубликован его экземпляром, и когда он был прочитан другим потоком.

Если вы хотите небезопасную публикацию, ваши основные параметры:

  • положить его в общую, но не поточно-ориентированную коллекцию. Это немного рискованно для вашего теста, так как сама коллекция может выдавать исключения
  • ввод в энергонезависимую статику
  • помещение его в массив (без использования AtomicReferenceArray)

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

У IBM есть экспериментальный javaagent, ConTest , который намеренно пытается использовать ошибки параллелизма. Некоторое время назад я попытался сделать это в заведомо опасной очереди (полагаю, это был намеренный тупик). Без ConTest очередь выходила из строя довольно редко; с этим неудачи возникали примерно в 30% случаев. Я не уверен, что ConTest лучше или хуже с гонками видимости памяти.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...