# Grasping the Synchronized Keyword in Java Multithreading
Written on
Chapter 1: Introduction to Multithreading in Java
In Java programming, managing multiple threads is essential for creating robust and efficient applications. The synchronized keyword plays a crucial role in regulating thread behavior and ensuring data integrity within a multi-threaded context. This article aims to clarify the synchronized keyword, detailing its significance, functionality, and effective application.
Section 1.1: The Purpose of the Synchronized Keyword
In a multi-threaded scenario, threads frequently interact and share resources such as variables or objects, which can lead to thread interference and memory consistency issues. The synchronized keyword acts as a modifier for methods and code blocks, ensuring that only one thread can execute a specific method or block at a time, thus mitigating concurrency issues.
Description: This video provides an overview of the synchronized keyword in Java multithreading, explaining its role and usage in programming.
Section 1.2: Understanding Synchronization
Consider a situation where multiple threads attempt to alter the same data simultaneously. Without proper control, this could result in inconsistent data states and difficult-to-trace bugs. The synchronized keyword addresses these problems by ensuring that only one thread can access a resource at any given moment.
How Synchronized Works
- Intrinsic Locks (Monitors): Every object in Java is associated with an intrinsic lock. When a thread wants to run a synchronized block of code, it must first acquire the lock for that object.
- Mutual Exclusivity: Once a thread holds the lock, no other thread can enter any synchronized block related to that object until the lock is released.
- Lock Release: The lock is automatically released when the thread exits the synchronized block, whether normally or due to an exception.
Chapter 2: Use Cases of the Synchronized Keyword
Section 2.1: Synchronized Methods
Synchronized methods are among the most prevalent uses of the synchronized keyword. Declaring a method as synchronized means that it obtains a lock on the entire object, allowing only one thread to execute that method at any time.
Example: Let’s illustrate synchronized methods with a banking application where multiple threads simulate users making transactions on the same bank account.
class SynchronizedBankAccount {
private double balance;
SynchronizedBankAccount(double initialBalance) {
this.balance = initialBalance;}
public synchronized void deposit(double amount) {
if (amount > 0) {
balance += amount;
System.out.println(Thread.currentThread().getName() + " deposited " + amount + ". New balance: " + balance);
}
}
public synchronized void withdraw(double amount) {
if (amount > 0 && balance >= amount) {
balance -= amount;
System.out.println(Thread.currentThread().getName() + " withdrew " + amount + ". New balance: " + balance);
} else {
System.out.println(Thread.currentThread().getName() + " attempted to withdraw " + amount + " but insufficient funds.");}
}
public double getBalance() {
return balance;}
}
public class SynchronizedMethodSimulation {
public static void main(String[] args) {
SynchronizedBankAccount account = new SynchronizedBankAccount(1000);
Thread t1 = new Thread(() -> {
account.deposit(300);
account.withdraw(50);
}, "User1");
Thread t2 = new Thread(() -> {
account.deposit(200);
account.withdraw(400);
}, "User2");
t1.start();
t2.start();}
}
This example shows how the SynchronizedBankAccount class manages a bank account with methods for depositing and withdrawing money. Both methods are synchronized to ensure that when one thread is executing either method, no other thread can do so, preserving data integrity.
Description: This video discusses the synchronized keyword in Java, focusing on synchronized blocks and methods, highlighting their practical applications.
Section 2.2: Synchronized Blocks
Consider a method as a room with a special area. Multiple threads can be in the room simultaneously, but only one can enter the special area at a time, similar to a synchronized block.
Example: In a ticket booking system where multiple users attempt to book seats at the same time, using synchronized blocks is crucial to ensure correct bookings and avoid conflicts.
public class TicketBookingSystem {
private int availableSeats;
public TicketBookingSystem(int totalSeats) {
this.availableSeats = totalSeats;}
public void bookSeat(int numberOfSeats) {
synchronized (this) {
System.out.println(Thread.currentThread().getName() + " attempting to book " + numberOfSeats + " seats");
if (numberOfSeats <= availableSeats) {
System.out.println(Thread.currentThread().getName() + " successfully booked " + numberOfSeats + " seats");
availableSeats -= numberOfSeats;
} else {
System.out.println(Thread.currentThread().getName() + " failed to book seats. Not enough seats available.");}
}
}
public static void main(String[] args) {
TicketBookingSystem bookingSystem = new TicketBookingSystem(10);
Thread user1 = new Thread(() -> bookingSystem.bookSeat(4), "User1");
Thread user2 = new Thread(() -> bookingSystem.bookSeat(7), "User2");
user1.start();
user2.start();}
}
This scenario illustrates how the TicketBookingSystem manages seat reservations. The bookSeat method is synchronized to ensure that when one user is booking seats, no other can book at the same time, thus preventing double bookings.
Section 2.3: Static Synchronization
Static synchronization refers to synchronizing a static method or block. Since static methods belong to the class rather than a specific instance, the lock is applied to the class object itself.
Example: A logging system where log entries from various parts of an application need to be recorded in a single log file demonstrates this concept.
public class Logger {
private static final Object lock = new Object();
public static void log(String message) {
synchronized (lock) {
System.out.println("Logging: " + message);}
}
public static void main(String[] args) {
Thread thread1 = new Thread(() -> Logger.log("Message from Thread 1"));
Thread thread2 = new Thread(() -> Logger.log("Message from Thread 2"));
thread1.start();
thread2.start();}
}
In this example, the log method is synchronized on a static lock object, allowing only one thread to write log messages at a time, ensuring data consistency.
Conclusion
In summary, different types of synchronization—static, instance, and block—serve various purposes, and selecting the appropriate type depends on the specific needs of the application, such as data access patterns, concurrency levels, and the desired balance between safety and performance.
Happy Learning!