Insights into Company
← Java

Multithreading

Understand the idea. Compare it. Explain it. Test yourself.

Question 11 of 14

What is volatile?

Solution and explanation

What it is

volatile is a field modifier that guarantees visibility and ordering: every read sees the most recent write by any thread, and writes are not reordered with surrounding operations in a way that breaks this. Without it, a thread may keep using a cached (stale) value.

Classic use: a stop flag

class Worker implements Runnable {
    private volatile boolean running = true;     // without volatile the loop may never stop

    public void run() {
        while (running) {
            processNextMessage();
        }
        System.out.println("Worker stopped");
    }

    public void stop() { running = false; }      // called from another thread
}

What volatile does NOT do

It does not make compound operations atomic:

private volatile int count = 0;
count++;          // still a race: read, add, write

Use AtomicInteger or synchronized for that.

volatile vs synchronized vs atomic

  • volatile: visibility only, no locking, very cheap. One writer / many readers of a simple flag or reference.
  • synchronized: mutual exclusion + visibility for multi-step updates.
  • AtomicInteger etc.: lock-free atomic updates of a single value + visibility.

Double-checked locking needs volatile

class ConfigHolder {
    private static volatile Config instance;
    static Config get() {
        if (instance == null) {
            synchronized (ConfigHolder.class) {
                if (instance == null) instance = new Config();   // volatile prevents seeing a half-built object
            }
        }
        return instance;
    }
}

Note: the Java Memory Model's "happens-before" rule: a write to a volatile field happens-before every later read of that field.

Practice explaining this
Question 12 of 14

What is the difference between wait() and sleep()?

Solution and explanation

The difference

  • Thread.sleep(ms) pauses the current thread for a time. It does not release any lock it holds. Static method of Thread.
  • obj.wait() pauses the current thread until another thread calls notify()/notifyAll() on the same object (or a timeout). It must be called while holding obj's lock and it releases that lock while waiting. Method of Object.

Comparison

  • Class: sleep – Thread; wait – Object.
  • Lock: sleep keeps locks; wait releases the monitor of obj.
  • Wake-up: sleep after the time; wait on notify/notifyAll/timeout/interrupt.
  • Calling context: sleep anywhere; wait only inside synchronized(obj), otherwise IllegalMonitorStateException.
  • Purpose: sleep for delays; wait for coordination between threads.

Producer–consumer with wait/notify

class OrderQueue {
    private final Queue<String> orders = new LinkedList<>();
    private final int capacity = 5;

    public synchronized void put(String order) throws InterruptedException {
        while (orders.size() == capacity) wait();     // releases the lock while full
        orders.add(order);
        notifyAll();                                  // wake consumers
    }

    public synchronized String take() throws InterruptedException {
        while (orders.isEmpty()) wait();              // releases the lock while empty
        String order = orders.poll();
        notifyAll();                                  // wake producers
        return order;
    }
}

Always call wait() in a while loop that rechecks the condition, because of spurious wake-ups.

sleep example

for (int attempt = 1; attempt <= 3; attempt++) {
    if (callPaymentGateway()) break;
    Thread.sleep(1000L * attempt);    // back off before retrying
}

Note: modern code uses BlockingQueue, CountDownLatch or CompletableFuture instead of raw wait/notify.

Practice explaining this
Question 13 of 14

What is ConcurrentHashMap?

Solution and explanation

What it is

ConcurrentHashMap is a thread-safe, high-performance implementation of Map designed for many threads reading and writing at once.

Why not HashMap or Hashtable?

  • HashMap is not thread-safe: concurrent writes can lose entries or corrupt the structure.
  • Hashtable and Collections.synchronizedMap lock the whole map for every operation, so threads queue up.
  • ConcurrentHashMap allows concurrent reads without locking and locks only a single bucket (bin) during writes (Java 8+ uses CAS operations and per-bin synchronization).

Example: counting job views from many request threads

ConcurrentHashMap<Long, LongAdder> views = new ConcurrentHashMap<>();

void recordView(long jobId) {
    views.computeIfAbsent(jobId, id -> new LongAdder()).increment();
}

long viewsFor(long jobId) {
    LongAdder counter = views.get(jobId);
    return counter == null ? 0 : counter.sum();
}

Atomic compound operations

map.putIfAbsent("java", 0);
map.merge("java", 1, Integer::sum);                    // atomic increment
map.compute("java", (k, v) -> v == null ? 1 : v + 1);
map.computeIfAbsent(key, k -> loadFromDatabase(k));   // loads once per key

Avoid check-then-act sequences like if (!map.containsKey(k)) map.put(k, v) – use the atomic methods above.

Characteristics

  • No null keys or values (null would be ambiguous with "absent").
  • Iterators are weakly consistent: they never throw ConcurrentModificationException and may or may not see concurrent updates.
  • size() is an estimate while other threads are modifying the map.

Complexity

  • get/put: O(1) average, like HashMap.

Note: a typical use is an in-memory cache or rate-limit counter shared by all request threads in a Spring Boot application.

Practice explaining this
Question 14 of 14

What is the difference between Runnable and Iterable?

Solution and explanation

They solve unrelated problems

  • Runnable (java.lang) represents a task to execute, usually in another thread. One method: void run().
  • Iterable<T> (java.lang) represents something you can loop over. One method: Iterator<T> iterator(). It enables the for-each loop.

Runnable: a unit of work

Runnable sendReminder = () -> System.out.println("Reminder sent at " + LocalTime.now());
new Thread(sendReminder).start();
executor.submit(sendReminder);

Iterable: a sequence of elements

class JobFeed implements Iterable<String> {
    private final List<String> titles = List.of("Java Developer", "QA Engineer", "Data Analyst");

    @Override
    public Iterator<String> iterator() { return titles.iterator(); }
}

for (String title : new JobFeed()) {      // works because JobFeed is Iterable
    System.out.println(title);
}
Output
Java Developer
QA Engineer
Data Analyst

Side by side

  • Purpose: Runnable – execution of a task; Iterable – traversal of elements.
  • Method: run() returns void; iterator() returns an Iterator.
  • Typical users: Thread, ExecutorService, schedulers vs for-each loops, streams (Iterable.spliterator), collections.
  • Both are functional-style interfaces (Runnable is annotated @FunctionalInterface; Iterable has one abstract method and default methods such as forEach).

Where they meet

You can iterate a collection of tasks and run each one:

List<Runnable> tasks = List.of(this::sendEmail, this::sendSms);
tasks.forEach(executor::submit);         // Iterable.forEach submitting Runnables

Note: this question checks that you understand interfaces by their contract: Runnable is about concurrency, Iterable is about collections.

Practice explaining this

Showing questions 11–14 of 14