memory visibility without synchronized or volatile

Is it correct to say without “synchronized” or “volatile” keyword, changes made by one thread will never be seen by another (or undeteministic)? I run the the following program many times in multi-core platform and results are different. Sometimes the program never terminates, which is the expected scenerio. But sometimes it exits with printing “1”.
JDK: jdk1.8.0_73
OS:CentOS Linux release 7.1.1503

public class VolatileTest implements Runnable {
    private int i = 0;

    public void run() {
        i++;
        i++;
    }

    public int get() {
        return i;
    }

    public static void main(String[] args) {
        ExecutorService executorService = Executors.newCachedThreadPool();
        VolatileTest volatileTest = new VolatileTest();
        executorService.execute(volatileTest);
        while (true) {
            int i = volatileTest.get();
            if (i % 2 != 0) { // 
                System.out.format("i: %s n", i);
                System.exit(0);
            }
        }
    }
}