Java Threads – Zero to Hero (Part 2)

Java Threads – Zero to Hero (Part 2)

posted 2 min read

Race Conditions & Why Synchronization Is Needed

A race condition occurs when two or more threads access and modify shared data at the same time, and the final outcome depends on the timing of their execution.
Because thread scheduling is unpredictable, this leads to inconsistent, incorrect, or unexpected results.

synchronization is needed:

  • To ensure only one thread accesses a critical section at a time.
  • To provide visibility guarantees, so changes made by one thread become visible to others.
  • To avoid data corruption and maintain correctness.
  • to bring order to the chaos caused by concurrent access.

Ways to Synchronize Threads

Java offers multiple synchronization mechanisms, from simple keywords to advanced APIs.

  1. synchronized keyword :Can be applied to methods or blocks (where threads give more granularity). Ensures mutual exclusion (one thread at a time). Uses intrinsic/monitor locks.
  1. Lock framework (ReentrantLock, ReadWriteLock, etc.) : More flexible than synchronized. Features
    like tryLock(), fairness policies, interruptible waiting.

  2. volatile keyword : Ensures visibility of shared variables, does NOT ensure atomicity, so not a full synchronization tool.

  3. Atomic classes (AtomicInteger, AtomicReference...) : Provide lock-free, thread-safe operations.

Inter-Thread Communication: wait(), notify(), notifyAll()

Java’s traditional inter-thread communication mechanism is built around Object class monitor methods.

  • wait() : Makes the current thread release the lock and go into a waiting state. Used when a thread needs to wait for a condition.

  • notify() : Wakes up one waiting thread (chosen by JVM). The awakened thread must re-acquire the lock before continuing.

  • notifyAll() : Wakes all threads waiting on the same lock. They compete to acquire the lock.

NOTE : All must be called inside synchronized blocks.

Thread Scheduling Basics

Java thread scheduling is preemptive and priority-based, but not guaranteed due to OS differences.

  1. Thread Priority
  • Values from 1 (MIN) to 10 (MAX), default 5.
  • Higher priority may get more CPU time, but JVM/OS may ignore priorities.
  1. Non-deterministic scheduling
  • You cannot predict which thread runs first or how long it runs.
  • Makes race conditions possible.
  1. yield()
  • Suggests that the current thread is willing to let others run.
  • Purely advisory; JVM may ignore it.
  1. sleep()
  • Pauses the thread for a specific time.
  • Does NOT release any locks.
  1. join()
    • One thread waits for another to finish.

Basic Producer-Consumer code to understand synchronisation

package org.example.thread;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import static org.example.thread.ImplementPC.MAX;
/**
 * Classic producer consumer problem
 * */
class ImplementPC {
    public static int num;
    public static int MAX = 10;
    public static boolean isSet = false;
    public synchronized void setNum(int num) {
            while(isSet) {
                try {
                    wait();
                } catch (InterruptedException e) {
                }
            }
                System.out.println("producing = " + num);
                this.num = num;
                isSet = true;
                notify();
    }
    public synchronized void getNum() {
            while(!isSet) {
                try {
                    wait();
                } catch (InterruptedException e){}
            }
            System.out.println("Consuming = " + num);
            isSet = false;
            notify();
    }
}
public class ProducerConsumer {
    public static void main(String[] args) {
        ImplementPC pc = new ImplementPC();
        ExecutorService executor = Executors.newFixedThreadPool(2);
        executor.submit(() ->{
            for(int i = 1; i < MAX; i++) {
                pc.setNum(i);
            }
        });
        executor.submit(() ->{
            for(int i = 1; i < MAX; i++) {
                pc.getNum();
            }
        });
        executor.shutdown();
    }

}

1 Comment

2 votes
1

More Posts

I’m a Senior Dev and I’ve Forgotten How to Think Without a Prompt

Karol Modelskiverified - Mar 19

Your Tech Stack Isn’t Your Ceiling. Your Story Is

Karol Modelskiverified - Apr 9

Java Threads Series – Zero to Hero (Part 1)

Madhu - Nov 13, 2025

Tuesday Coding Tip 02 - Template with type-specific API

Jakub Neruda - Mar 10

Why Email-Only Contact Forms Are Failing in 2026 (And What Developers Should Do Instead)

JayCode - Mar 2
chevron_left

Related Jobs

View all jobs →

Commenters (This Week)

2 comments
1 comment

Contribute meaningful comments to climb the leaderboard and earn badges!