← Back to Advanced Java Course | Chapter 11: Advanced & Security | Lesson 16 of 19

Java Interview Questions Advanced

Volatile vs. Atomic

The volatile keyword guarantees memory visibility for a variable -- every thread reads its current value directly rather than from a possibly-stale per-thread cache -- but it does not make compound operations like increment atomic. Atomic classes go further, guaranteeing the actual operation (via CAS) is atomic too.

Example: Volatile vs. Atomic

java
import java.util.concurrent.atomic.AtomicInteger;
public class Main {
	static volatile boolean flag = false; // guarantees visibility, not atomicity of compound ops
	static AtomicInteger counter = new AtomicInteger(0); // guarantees the operation itself is atomic
	public static void main(String[] args) {
		flag = true;
		counter.incrementAndGet();
		System.out.println(flag + " " + counter.get());
	}
}

Optimistic vs. Pessimistic Locking

Pessimistic locking assumes conflicts will happen and blocks other threads from touching a resource while one thread holds it. Optimistic locking assumes conflicts are rare, lets threads proceed without locking, and instead uses an atomic compare-and-swap loop to detect and retry if a conflicting modification actually occurred.

Example: Optimistic vs. Pessimistic Locking

java
import java.util.concurrent.atomic.AtomicInteger;
public class Main {
	static final Object pessimisticLock = new Object();
	static AtomicInteger optimisticValue = new AtomicInteger(0);
	public static void main(String[] args) {
		synchronized (pessimisticLock) { // blocks other threads while held
			System.out.println("Pessimistic: locked");
		}
		int current;
		do {
			current = optimisticValue.get();
		} while (!optimisticValue.compareAndSet(current, current + 1)); // retries instead of blocking
		System.out.println("Optimistic: " + optimisticValue.get());
	}
}

ThreadLocal Memory

ThreadLocal lets you store a variable such that each thread accessing it sees its own completely independent copy, rather than a value shared across threads. This is a common way to avoid multi-threaded data leaks for state that's inherently per-thread, like a request context or a formatter instance.

Example: ThreadLocal Memory

java
public class Main {
	static ThreadLocal<Integer> threadLocal = ThreadLocal.withInitial(() -> 0);
	public static void main(String[] args) throws InterruptedException {
		threadLocal.set(42); // only visible to this thread
		Thread other = new Thread(() -> System.out.println("Other thread sees: " + threadLocal.get()));
		other.start();
		other.join();
		System.out.println("Main thread sees: " + threadLocal.get());
	}
}

Safe Serialization

To secure deserialization against malicious input, validate an object's reconstructed state inside a custom readObject() method before trusting it, or restrict which fields participate in serialization at all via serialPersistentFields, rather than blindly trusting whatever bytes arrive.

Example: Safe Serialization

java
import java.io.*;
public class Main {
	static class User implements Serializable {
		int age;
		private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
			in.defaultReadObject();
			if (age < 0) throw new InvalidObjectException("age cannot be negative"); // validate before trusting
		}
	}
	public static void main(String[] args) {
		System.out.println("readObject() validates reconstructed state before trusting it");
	}
}

Dynamic Method Dispatch

Dynamic Method Dispatch is the mechanism that resolves which overridden method implementation actually runs at runtime, based on the object's real (dynamic) type rather than the reference's declared (static) type -- this is the core mechanism that makes runtime polymorphism work in Java.

Example: Dynamic Method Dispatch

java
public class Main {
	static class Animal {
		String sound() { return "..."; }
	}
	static class Dog extends Animal {
		String sound() { return "Woof"; } // overridden implementation
	}
	public static void main(String[] args) {
		Animal a = new Dog(); // declared type Animal, real type Dog
		System.out.println(a.sound()); // resolved at runtime based on the real type
	}
}

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.