Skip to content

Latest commit

 

History

History
95 lines (62 loc) · 3.73 KB

File metadata and controls

95 lines (62 loc) · 3.73 KB

Synchronization in Java

TOPIC OVERVIEW

Synchronization is a fundamental mechanism in Java multithreading used to control access to shared resources by multiple threads simultaneously. By ensuring that only one thread can execute a critical section of code at a time, it prevents race conditions, data corruption, and inconsistent results.

Why this topic is important:

  • It is a cornerstone concept for thread safety in concurrent programming.
  • It is essential for protecting shared data integrity.
  • It is a critical requirement for building robust, multi-threaded applications.
  • It is a standard subject in technical interviews and practical software engineering tasks.

1. DEFINITION AND PURPOSE

Synchronization enforces exclusive access to a shared resource, ensuring that a "critical section"—a block of code accessing shared data—is executed by only one thread at a time. Without this protection, concurrent access often leads to race conditions, where the final outcome depends on the unpredictable timing of thread execution.


2. TYPES OF SYNCHRONIZATION

Java provides three primary mechanisms to implement synchronization:

  1. Synchronized Method: The synchronized keyword is applied to a method signature, locking the entire method for the object instance.
  2. Synchronized Block: The synchronized(object) { ... } construct locks only a specific block of code, allowing for more granular control.
  3. Static Synchronization: Applied to static methods, this locks the class-level monitor rather than an object instance monitor.

3. PROGRAMMATIC EXAMPLES

Example 1: The Problem of Shared Data (No Synchronization)

This example demonstrates a race condition where two threads increment a shared counter without protection, often resulting in an incorrect final count.

class Counter {
    int count = 0;
    void increment() { count++; }
}

public class SyncExample1 {
    public static void main(String[] args) throws InterruptedException {
        Counter counter = new Counter();
        Thread t1 = new Thread(() -> { for(int i=0; i<1000; i++) counter.increment(); });
        Thread t2 = new Thread(() -> { for(int i=0; i<1000; i++) counter.increment(); });
        
        t1.start(); t2.start();
        t1.join(); t2.join();
        System.out.println("Final count: " + counter.count); // Likely < 2000
    }
}

Example 2: Thread-Safety with Synchronized Method

By adding the synchronized keyword, the increment() method becomes thread-safe, ensuring accurate results.

class Counter {
    int count = 0;
    synchronized void increment() { count++; }
}

Example 3: Precision Control with Synchronized Block

Using a block allows developers to protect only the necessary lines of code, which can improve performance.

void update() {
    synchronized (this) {
        value++; // Critical section protected by object lock
    }
}

4. IMPORTANT CONSIDERATIONS

  • Performance Impact: Excessive synchronization can lead to performance bottlenecks, as threads must wait for the lock to be released.
  • Deadlock Risk: Incorrect usage of synchronization can result in deadlocks, where threads are indefinitely blocked waiting for each other.
  • Granularity: Prefer synchronized blocks over synchronized methods when only a portion of a method needs to be thread-safe to maintain efficiency.

5. FINAL SUMMARY

Synchronization is indispensable for managing shared resources in Java. By utilizing synchronized methods or blocks, developers ensure that critical operations remain thread-safe, protecting the integrity of the application's data. Proper synchronization balances the need for data safety with the performance requirements of concurrent execution.