Dúvida sobre Concorrência: atomic, volatile e synchronized

Olá galera,

iniciei um trabalho que envolve explicar o funcionamento do paralelismo na linguagem escolhida, e como escolhi Java estou tentando entender como funciona hj e como era antigamente…

pois bem, após o java 1.5 foram introduzidos estes conceitos para facilitar a vida do programador, mas estou com dificuldade em compreender o uso do volatile…

após ler e reler alguns artigos pela net, em especial este post: http://www.javamex.com/tutorials/synchronization_volatile.shtml e este doc: http://java.sun.com/docs/books/jls/second_edition/html/classes.doc.html#36930

Cheguei a seguinte conclusão:
Atomic classes e métodos/blocos synchronized garantem que não ocorra o problema da atualização perdida, mas não garantem que a leitura de ambos para impressão captará o mesmo valor, ex: se há várias threads incrementando duas variáveis atomicas, a leitura das duas ocorre em tempos diferentes, então por mais que a operação ocorra em 1 ciclo de cpu, ainda assim a segunda leitura poderá trazer um valor diferente, mas não inconsistente

Mas deixando o ponto acima de lado, o que não ficou claro para mim foi qual a utilidade da keyword volatile. Alguém poderia me dar algum exemplo simples e conciso sobre seu uso? Já li várias coisas mas continuo sem entender sua utilidade…

No site da sun diz o seguinte:
" two could occasionally print a value for j that is greater than the value of i, because the example includes no synchronization and, under the rules explained in §17, the shared values of i and j might be updated out of order.
One way to prevent this out-or-order behavior would be to declare methods one and two to be synchronized (§8.4.3.6):

class Test {
static int i = 0, j = 0;
static synchronized void one() { i++; j++; }
static synchronized void two() {
System.out.println(“i=” + i + " j=" + j);
}
}
This prevents method one and method two from being executed concurrently, and furthermore guarantees that the shared values of i and j are both updated before method one returns. Therefore method two never observes a value for j greater than that for i; indeed, it always observes the same value for i and j.
Another approach would be to declare i and j to be volatile:

class Test {
static volatile int i = 0, j = 0;
static void one() { i++; j++; }
static void two() {
System.out.println(“i=” + i + " j=" + j);
}
}
This allows method one and method two to be executed concurrently, but guarantees that accesses to the shared values for i and j occur exactly as many times, and in exactly the same order, as they appear to occur during execution of the program text by each thread. Therefore, the shared value for j is never greater than that for i, because each update to i must be reflected in the shared value for i before the update to j occurs.
It is possible, however, that any given invocation of method two might observe a value for j that is much greater than the value observed for i, because method one might be executed many times between the moment when method two fetches the value of i and the moment when method two fetches the value of j. See §17 for more discussion and examples." fonte: http://java.sun.com/docs/books/jls/second_edition/html/classes.doc.html#36930