Q:

Understanding JVM guarantees for immutable objects

Java Concurrency in Practice has the following example:
@ThreadSafe
public class VolatileCachedFactorizer implements Servlet {
    private volatile OneValueCache cache = new OneValueCache(null, null);

    public void service(ServletRequest req, ServletResponse resp) {
        BigInteger i = extractFromRequest(req);
        BigInteger[] factors = cache.getFactors(i);
        if (factors == null) {
            factors = factor(i);
            cache = new OneValueCache(i, factors);
        }
            encodeIntoResponse(resp, factors);
        }
    }
}

The OneValueCache is immutable.
From what I understand using volatile ensures that all threads see up-to-date reference stored in the cache variable.
At the same time it says that 

Immutable objects can be used safely by any thread without additional
      synchronization, even when synchronization is not used to publish them.

For me it says that we actually don't need that volatile above that we used for synchronization.
JLS also has the following example:
class FinalFieldExample {
    final int x;
    int y; 

    static FinalFieldExample f;

    public FinalFieldExample() {
        x = 3;
        y = 4;
    }

    static void writer() {
        f = new FinalFieldExample();
    }

    static void reader() {
        if (f != null) {
            int i = f.x; // guaranteed to see 3
            int j = f.y; // could see 0
        }
    }
}

They don't use volatile for field f. Doesn't it mean that other threads can see f as null and never see the created instance?
Could someone explain?

A:

From what I understand using volatile ensures that all threads see up-to-date reference stored in the cache variable.

That's right, but this concept applies to the variable while the statement quoted from JCiP

Immutable objects can be used safely by any thread without additional
  synchronization, even when synchronization is not used to publish them.

does not apply to a variable but to the object itself. It means that a thread will always see a fully constructed object without any data races independent on how it is published because as JCiP states

Immutable objects can be published through any mechanism.

Now in regard to your second example:

Doesn't it mean that other threads can see f as null and never see the created instance?

That is right. If one thread calls writer() other thready may see f as null or f.y as 0 because it does not respect safe publication:

3.5.3. Safe Publication Idioms
To publish an object safely, both the reference to the object and the
  object's state must be made visible to other threads at the same time.
  A properly constructed object can be safely published by:

Initializing an object reference from a static initializer;
Storing a reference to it into a volatile field or AtomicReference;
Storing a reference to it into a final field of a properly constructed > object; or
Storing a reference to it into a field that is properly guarded by a lock.

