На самом деле вам не гарантировано видеть x = 10 в соответствии с JMM.
Например, если у вас есть
Test test = null;
Thread 1 -> test = new Test();
Thread 2 -> test.x == // even though test != null, x can be seen as 0 if the
// write of x hasn't yet occur
Теперь, если у вас было
class Test{
int y = 3;
volatile x = 10;
}
Если поток-2 читает x == 10, поток-2 гарантированно читает y == 3
Чтобы ответить на ваш второй вопрос.
Наличие последнего поля приведет к созданию storetore после конструктора и перед публикацией, поэтому наличие поля final фактически гарантирует, что вы увидите x = 10.
Редактировать: Как заметил ишавит. Вы теряете отношение «происходит до», о котором я упоминал в моем первом примере с последними полями, то есть, как выразился yshavit, если thread-2 читает x == 10, он может не прочитать y == 3, где x - конечное поле.