Object-Level Locking In MultiThreading:
Introduction:
In multithreaded Java applications, when multiple threads access shared resources concurrently, it can lead to race conditions and data inconsistencies.
To prevent such issues, Java provides a built-in synchronization mechanism that includes object-level locking.
Object-level locking allows you to control concurrent access to critical sections of code by synchronizing on specific objects.
Understanding Object-Level Locking:
Object-level locking, also known as intrinsic locking, is based on the concept of intrinsic locks (monitor locks) associated with each Java object.
When a synchronized method or synchronized block is executed, it acquires the intrinsic lock of the object it is synchronized on.
This lock ensures that only one thread can execute the synchronized code segment at any given time for that particular object.
By synchronizing access to shared resources using object-level locking, we can prevent race conditions and maintain data integrity, even in the presence of multiple threads.
Syntax of Object-Level Locking:
- Synchronized Method:
public synchronized void synchronizedMethod() {
// Synchronized method body
// Only one thread can execute this method at a time
}
- Synchronized Block:
public void someMethod() {
// Non-critical code here
synchronized (sharedObject) {
// Synchronized block
// Only one thread can execute this block at a time for the sharedObject
}
// Non-critical code here
}
Example: Implementing Object-Level Locking
Let's illustrate object-level locking with a simple example.
Consider a shared BankAccount
class that allows multiple threads to deposit and withdraw money concurrently:
class BankAccount {
private int balance = 0;
public void deposit(int amount) {
synchronized (this) {
balance += amount;
}
}
public void withdraw(int amount) {
synchronized (this) {
if (amount <= balance) {
balance -= amount;
}
}
}
public int getBalance() {
return balance;
}
}
In this example, the deposit() and withdraw() methods use synchronized blocks, synchronizing on the this reference (the BankAccount object).
This ensures that only one thread can execute the deposit or withdraw operation at a time for the same BankAccount object.
Creating Multiple Threads:
Now, let's create multiple threads to simulate concurrent transactions:
public class Main {
public static void main(String[] args) {
BankAccount account = new BankAccount();
Runnable depositTask = () -> {
for (int i = 0; i < 10000; i++) {
account.deposit(10);
}
};
Runnable withdrawTask = () -> {
for (int i = 0; i < 5000; i++) {
account.withdraw(5);
}
};
int numThreads = 4;
Thread[] depositThreads = new Thread[numThreads];
Thread[] withdrawThreads = new Thread[numThreads];
for (int i = 0; i < numThreads; i++) {
depositThreads[i] = new Thread(depositTask);
withdrawThreads[i] = new Thread(withdrawTask);
depositThreads[i].start();
withdrawThreads[i].start();
}
// Wait for all threads to complete
try {
for (int i = 0; i < numThreads; i++) {
depositThreads[i].join();
withdrawThreads[i].join();
}
}
catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Final Balance: " + account.getBalance());
}
}
In this example, we create four threads for deposit and four threads for withdrawal. Each deposit thread deposits 10 units of money 10,000 times, while each withdrawal thread withdraws 5 units of money 5,000 times.
Thanks to the synchronized blocks in the deposit() and withdraw() methods, object-level locking ensures that the deposit and withdrawal operations are executed atomically and without interference from other threads. As a result, the final balance remains accurate and avoids any data inconsistencies.
Conclusion:
Object-level locking is a fundamental feature of Java's synchronization mechanism that allows developers to ensure thread safety and synchronization in multithreaded applications.
By synchronizing on specific objects, we can control concurrent access to shared resources and prevent race conditions.
Proper use of object-level locking ensures data integrity, enhances thread safety, and results in robust and reliable multithreaded Java programs.
0 Comments