Data Types and Operators
(Non-Primitive Data Types)
Control Flow Statements
Conditional Statements
Looping Statements
Branching Statements
Object-Oriented Programming (OOP)
Exception Handling
Collections Framework
Overview of Collections
Java I/O
Multithreading
GUI Programming with Swing
Advanced Topics
JAVA CODE
Java Basics
Working with Objects
Arrays, Conditionals, and Loops
Creating Classes and Applications in Java
More About Methods
Java Applet Basics
Graphics, Fonts, and Color
Simple Animation and Threads
More Animation, Images, and Sound
Managing Simple Events and Interactivity
Creating User Interfaces with the awt
Windows, Networking, and Other Tidbits
Modifiers, Access Control, and Class Design
Packages and Interfaces
Exceptions
Multithreading
Streams and I/O
Using Native Methods and Libraries
Under the Hood
Java Programming Tools
Working with Data Structures in Java
Advanced Animation and Media
Fun with Image Filters
Client/Server Networking in Java
Emerging Technologies
appendix A :- Language Summary
appendix B :- Class Hierarchy Diagrams
appendix C The Java Class Library
appendix D Bytecodes Reference
appendix E java.applet Package Reference
appendix F java.awt Package Reference
appendix G java.awt.image Package Reference
appendix H java.awt.peer Package Reference
appendix I java.io Package Reference
appendix J java.lang Package Reference
appendix K java.net Package Reference
appendix L java.util Package Reference
In Java, synchronization is a mechanism that ensures that two or more concurrent threads do not simultaneously execute some particular program segment known as a critical section. Threads are allowed to execute the critical section one by one.

Synchronized Methods

A synchronized method in Java is one that uses the synchronized keyword in its declaration. This ensures that only one thread can execute this method on the same object instance at the same time. Here’s how it works:

  1. When a thread invokes a synchronized method, it automatically acquires the intrinsic lock (or monitor) for that object.
  2. All other threads that invoke synchronized methods on the same object are blocked until the first thread releases the lock.

Example

				
					class Counter {
    private int count = 0;

    // Synchronized method to increment count
    public synchronized void increment() {
        count++;
    }

    // Synchronized method to get count
    public synchronized int getCount() {
        return count;
    }
}

public class Main {
    public static void main(String[] args) {
        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();

        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("Count: " + counter.getCount()); // Should print 2000
    }
}

				
			
In this example, the ‘increment‘ and ‘getCount‘ methods are synchronized, so they ensure that only one thread can access them at a time for a given ‘Counter‘ object instance.

Synchronized Blocks

Synchronized blocks are used to synchronize only a part of the method. They are more flexible and efficient as you can control the scope of the lock. This allows you to lock only the critical section of the code, rather than the entire method.

Example

				
					class Counter {
    private int count = 0;
    private final Object lock = new Object();

    // Method with synchronized block to increment count
    public void increment() {
        synchronized (lock) {
            count++;
        }
    }

    // Method with synchronized block to get count
    public int getCount() {
        synchronized (lock) {
            return count;
        }
    }
}

public class Main {
    public static void main(String[] args) {
        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();

        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("Count: " + counter.getCount()); // Should print 2000
    }
}

				
			
In this example, only the critical section of the ‘increment‘ and ‘getCount‘ methods is synchronized, thus potentially improving performance by reducing the duration for which the lock is held.

Key Points

  • Synchronized Method: Locks the entire method. Only one thread can access a synchronized method of an object at a time.
  • Synchronized Block: Locks a specific section of code within a method. More granular control over the locking mechanism.

Choosing between Synchronized Method and Synchronized Block:

  • Use synchronized methods when the entire method needs to be synchronized and simplicity is more important than performance.
  • Use synchronized blocks when only part of the method needs to be synchronized or when you need more fine-grained control over the lock, potentially improving performance by reducing the scope and duration of the lock.

In both cases, synchronization ensures thread safety by allowing only one thread to execute a critical section at a time, preventing race conditions and ensuring data consistency.

Scroll to Top