Когда Java возражает, становятся непустыми во время конструкции?

Использование subscribe, вероятно, лучший способ. Однако, если вы действительно хотите использовать then (что может пригодиться, если вам нужно объединить вызовы в цепочку), вы можете преобразовать наблюдаемое в обещание, используя метод toPromise()

this.http.get('https://somewebsite.com',
{
    //... 
}).toPromise().then(response => {
       //...
        });
17
задан Joshua 9 February 2019 в 16:10
поделиться

6 ответов

Если другой поток должен был проверить someObject переменная "во время" конструкции, я полагаю, что она может (из-за причуд в модели памяти), посмотрите частично инициализированный объект. Новое (с Java 5) модель памяти означает, что любые заключительные поля должны быть установлены на свои значения, прежде чем объект станет видимым к другим потокам (пока ссылка на недавно созданный объект не сбегает от конструктора никаким другим способом), но кроме того нет многих гарантий.

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

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

Править: В целях перепроверяемой блокировки можно сойти с рук это, если поле volatile и если Вы используете Java 5 или выше. До Java 5 модель памяти не была достаточно сильна для этого. Необходимо получить шаблон, точно правильный все же. Посмотрите Эффективный Java, 2-й выпуск, объект 71 для получения дополнительной информации.

Править: Вот мое обоснование для приведения доводов против встраивания Aaron, являющегося видимым в единственном потоке. Предположим, что мы имеем:

public class FooHolder
{
    public static Foo f = null;

    public static void main(String[] args)
    {
        f = new Foo();
        System.out.println(f.fWasNull);
    }
}

// Make this nested if you like, I don't believe it affects the reasoning
public class Foo
{
    public boolean fWasNull;

    public Foo()
    {
        fWasNull = FooHolder.f == null;
    }
}

Я полагаю, что это будет всегда сообщать true. От раздела 15.26.1:

Иначе три шага требуются:

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

Затем от раздела 17.4.5:

Два действия могут быть заказаны происхождением - перед отношениями. Если одно действие происходит - перед другим, то первое видимо к и заказанный перед вторым.

Если у нас есть два X и Y действий, мы пишем hb (x, y), чтобы указать, что x происходит - прежде y.

  • Если X и Y являются действиями того же потока, и x существует прежде y порядка программы, то hb (x, y).
  • Существует происхождение - перед краем от конца конструктора объекта к запуску финализатора (§12.6) для того объекта.
  • Если действие x синхронизируется - со следующим действием y, то у нас также есть hb (x, y).
  • Если hb (x, y) и hb (y, z), то hb (x, z).

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

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

16
ответ дан 30 November 2019 в 13:05
поделиться

someObject станет не -null в какой-то момент во время конструкции. Как правило, существует два случая:

  1. Оптимизатор встроил конструктора
  2. Конструктор не встраивается.

В первом случае VM выполнит этот код (псевдокод):

someObject = malloc(SomeClass.size);
someObject.field = ...
....

Так в этом случае, someObject не null и это указывает на память, которая не составляет инициализированных 100%, а именно, не, весь код конструктора был выполнен! Поэтому перепроверяемая блокировка не работает.

Во втором случае будет работать код от конструктора, ссылка будет пасоваться назад (точно так же, как в нормальном вызове метода), и someObject будет установлен на значение refernce, в конце концов, и каждый код init работал.

Проблема состоит в том, что нет никакого способа сказать Java не присваиваться someObject рано. Например, Вы могли попробовать:

SomeClass tmp = new SomeClass();
someObject = tmp;

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

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

[РЕДАКТИРОВАНИЕ] Здесь является хорошей статьей, которая объясняет, что происходит: http://www.ibm.com/developerworks/java/library/j-dcl.html

PS: книга "Эффективный Java, Второй Выпуск" Joshua Bloch содержит решение для Java 5 и:

private volatile SomeClass field;
public SomeClass getField () {
    SomeClass result = field;
    if (result == null) { // First check, no locking
        synchronized(this) {
            result = field;
            if (result == null) { // second check with locking
                field = result = new SomeClass ();
            }
        }
    }
    return result;
}

Выглядит странным, но должен работать над каждым Java VM. Обратите внимание, что каждый бит важен; если Вы опускаете двойное, присваиваются, Вы или получите плохую производительность или частично инициализированные объекты. Для полного объяснения купите книгу.

9
ответ дан 30 November 2019 в 13:05
поделиться

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

2
ответ дан 30 November 2019 в 13:05
поделиться

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

Object o = null;
try {
    o = new CtorTest();
} catch (Exception e) {
    assert(o == null); // i will be null
}

где

class CtorTest {
    public CtorTest() {
        throw new RuntimeException("Ctor exception.");
    }
}

Удостоверьтесь, что синхронизировались на другом объекте, не создаваемом том.

0
ответ дан 30 November 2019 в 13:05
поделиться

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

public class Test {

  private static SlowlyConstructed slowlyConstructed = null;

  public static void main(String[] args) {
    Thread constructor = new Thread() {
      public void run() {
        Test.slowlyConstructed = new SlowlyConstructed();
      }
    };
    Thread checker = new Thread() {
      public void run() {
        for(int i = 0; i < 10; i++) {
          System.out.println(Test.slowlyConstructed);
          try { Thread.sleep(1000); }
          catch(Exception e) {}
        }
      }
    };

    checker.start();
    constructor.start();
  }

  private static class SlowlyConstructed {
    public String s1 = "s1 is unset";
    public String s2 = "s2 is unset";

    public SlowlyConstructed() {
      System.out.println("Slow constructor has started");
      s1 = "s1 is set";
      try { Thread.sleep(5000); }
      catch (Exception e) {}
      s2 = "s2 is set";
      System.out.println("Slow constructor has finished");
    }

    public String toString() {
      return s1 + ", " + s2;
    }
  }
}

Вывод:

null
Slow constructor has started
null
null
null
null
null
Slow constructor has finished
s1 is set, s2 is set
s1 is set, s2 is set
s1 is set, s2 is set
s1 is set, s2 is set
0
ответ дан 30 November 2019 в 13:05
поделиться

Для Вашего первого примера: someObject становится непустым ПОСЛЕ ТОГО, КАК конструктор завершился. Если бы Вы проверили бы от другого потока, someObject стал бы непустым после того, как конструктор закончил. Остерегайтесь, Вы никогда не должны получать доступ к несинхронизируемым объектам от различных потоков, таким образом, Ваш пример не должен быть реализован тот путь в реальном коде.

Для второго примера someObject никогда не был бы пустым, поскольку он создается ПОСЛЕ ТОГО, КАК сам SomeClass создается, и someObject является created&initialized с недавно созданным объектом. То же здесь для потоков: не получайте доступ к этой переменной от различных потоков без синхронизации!

-1
ответ дан 30 November 2019 в 13:05
поделиться
Другие вопросы по тегам:

Похожие вопросы: