Q:

Где использовать volatile

Когда можно использовать volatile, если он не обеспечивает атомарность чтения и записи как Atomic'и.
Мне не понятно в чем разница между volatile и synchronized, если и то и другое обеспечивает синхронизацию между кэшами ядер процессора.

A:

Модификатор volatile гарантирует видимость операций с полем и сохранение их последовательности. Волатильную переменную можно, например, использовать как флаг завершения работы потока:
public class Main {
    private static volatile boolean run = true;

    public static void main(String[] args) throws Exception {
        new Thread(() -> {
            long x = 0;
            while (run) {
                System.out.println(++x);
                try {
                    Thread.sleep(1000);
                }
                catch (InterruptedException exc) {}
            }
        }).start();

        Scanner scanner = new Scanner(System.in);
        while (run) {
            String line = scanner.nextLine();
            if ("exit".equals(line))
                run = false;
        }
    }
}

Без модификатора volatile у вас нет гарантии, что поток выводящий значения переменной x когда-нибудь заметит, что главный поток изменил состояние переменной run.
Синхронизация гарантирует видимость операций, сохранение их последовательности и атомарность.
public class Main {
    private static int x = 0;
    private static int y = 1000;

    private static synchronized void transfer() {
        ++x;
        --y;
    }

    public static void main(String[] args) throws Exception {
        for (int a = 0; a < 10; a++) {
            new Thread(() -> {
                for (int b = 0; b < 100; b++) {
                    transfer();
                }
            }).start();
        }

        System.out.println(x);
        System.out.println(y);
    }
}

Без модификатора synchronized у вас нет гарантии, что значение переменной x будет увеличено на столько же, на сколько уменьшено значение переменной y, так как порядок и продолжительность выполнения потоков непредсказуемы.
Во втором примере volatile не поможет. В первом может помочь использование синхронизации, но тогда на каждой итерации один из потоков будет блокировать мьютекс, а второй потом будет останавливаться, пока мьютекс не будет освобождён. Это существенно медленнее проверки состояния волатильной переменной.

