Tutorials Logic, IN info@tutorialslogic.com

Multithreading in Java Thread, Runnable, Sync

Creating Threads

Prefer Runnable or ExecutorService over extending Thread for most application code. Separating the task from the thread improves design.

Runnable Thread

Runnable Thread
public class ThreadDemo {
    public static void main(String[] args) {
        Runnable task = () -> {
            for (int i = 1; i <= 3; i++) {
                System.out.println(Thread.currentThread().getName() + ": " + i);
            }
        };

        Thread thread = new Thread(task, "worker-1");
        thread.start();
    }
}

Race Conditions and synchronized

A race condition happens when the result depends on timing between threads. synchronized protects critical sections by allowing one thread at a time.

Synchronized Counter

Synchronized Counter
class Counter {
    private int value;

    synchronized void increment() {
        value++;
    }

    int getValue() {
        return value;
    }
}

ExecutorService

ExecutorService manages worker threads for you. It is better than manually creating many Thread objects.

ExecutorService - Java Example

ExecutorService - Java Example
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ExecutorDemo {
    public static void main(String[] args) {
        ExecutorService executor = Executors.newFixedThreadPool(2);
        executor.submit(() -> System.out.println("Task 1"));
        executor.submit(() -> System.out.println("Task 2"));
        executor.shutdown();
    }
}

Thread Safety Tools

Java provides volatile, locks, atomic classes, concurrent collections, and higher-level utilities. Prefer higher-level utilities when possible.

  • Use AtomicInteger for simple counters.
  • Use ConcurrentHashMap for concurrent key-value access.
  • Avoid sharing mutable state when possible.
  • Prefer immutable objects for safe sharing.

Visibility, Ordering, and Safe Publication

A race is not limited to two threads incrementing the same number. One thread may observe stale fields or an object before construction is safely published. synchronized, volatile, final-field guarantees, locks, concurrent collections, thread start, and thread join create specific happens-before relationships that make writes visible in a defined order.

volatile is suitable for visibility of an independent state value, but it does not turn a read-modify-write sequence into one atomic action. Use an atomic type, a lock, or confinement when multiple fields must change together. Prefer immutable messages and task ownership so fewer invariants depend on shared mutation.

  • Document the lock or thread that owns each mutable invariant.
  • Never call unknown blocking code while holding a broad lock.
  • Use stress tests and thread diagnostics; timing sleeps do not prove correctness.

Cancellation, Executors, and Shutdown

Interruption is a cooperative cancellation signal. Blocking methods may throw InterruptedException, and code that cannot complete the cancellation should restore the interrupt status rather than swallowing it. A task must also release locks, files, sockets, and partial state when cancellation occurs.

ExecutorService separates task submission from thread creation, but its queue size, rejection policy, thread count, and shutdown behavior are part of the application contract. Bound queues when overload must be visible. Stop accepting work, request shutdown, wait for a defined period, and escalate deliberately if tasks do not finish. Capture thread dumps when progress stops so deadlock, starvation, and blocked I/O can be distinguished.

  • Name executor threads so logs and dumps identify the workload.
  • Propagate task failures from Future or structured task boundaries.
  • Test rejection, cancellation, timeout, and orderly shutdown paths.
Before you move on

Multithreading in Java Thread, Runnable, Sync Mastery Check

4 checks
  • Prefer Runnable or ExecutorService over extending Thread for most application code.
  • Separating the task from the thread improves design.
  • It is better than manually creating many Thread objects.
  • Java provides volatile, locks, atomic classes, concurrent collections, and higher-level utilities.

Core Java Questions Learners Ask

A process is an independent program with its own memory space. A thread is a lightweight unit within a process that shares memory with other threads. Threads are faster to create and communicate, but require synchronization.

A synchronized method locks the entire object (this). A synchronized block locks only a specific object for a specific section of code - more granular and better for performance.

Deadlock occurs when Thread A holds Lock 1 and waits for Lock 2, while Thread B holds Lock 2 and waits for Lock 1. Prevent it by: always acquiring locks in the same order, using tryLock() with timeout, or using higher-level concurrency utilities.

Browse Free Tutorials

Explore 500+ free tutorials across 20+ languages and frameworks.