Introduction
Synchronization in Java is a powerful mechanism that allows you to control the access of multiple threads to shared resources. It ensures that only one thread can access the resource at a time, preventing data inconsistency and race conditions. This is essential in a multithreading environment where threads often share resources like variables, arrays, or objects.
Table of Contents
- Overview of Synchronization
- Synchronized Methods
- Synchronized Blocks
- Static Synchronization
- Example: Synchronized Method
- Example: Synchronized Block
- Reentrant Synchronization
- Synchronization and Performance
- Conclusion
1. Overview of Synchronization
In Java, the synchronized
keyword can be used to synchronize access to critical sections of code. There are two main types of synchronization:
- Synchronized Methods: Entire methods are marked as synchronized, ensuring that only one thread can execute them at a time.
- Synchronized Blocks: Specific blocks of code are marked as synchronized, providing more granular control over the synchronization.
2. Synchronized Methods
A synchronized method ensures that only one thread can execute it at a time for a particular object. The lock is held on the object instance on which the method is called.
Syntax:
public synchronized void synchronizedMethod() {
// synchronized code
}
3. Synchronized Blocks
A synchronized block is a block of code within a method that is synchronized on a specified object. This allows you to lock only the critical section of the code rather than the entire method.
Syntax:
public void method() {
synchronized (this) {
// synchronized code
}
}
4. Static Synchronization
Static synchronization ensures that a class-level lock is acquired, so all instances of the class share the same lock.
Syntax:
public static synchronized void staticSynchronizedMethod() {
// synchronized code
}
Or using a synchronized block:
public static void staticMethod() {
synchronized (ClassName.class) {
// synchronized code
}
}
5. Example: Synchronized Method
Example:
class Counter {
private int count = 0;
public synchronized void increment() {
count++;
}
public int getCount() {
return count;
}
}
public class SynchronizedMethodExample {
public static void main(String[] args) throws InterruptedException {
Counter counter = new Counter();
Runnable task = () -> {
for (int i = 0; i < 1000; i++) {
counter.increment();
}
};
Thread thread1 = new Thread(task);
Thread thread2 = new Thread(task);
thread1.start();
thread2.start();
thread1.join();
thread2.join();
System.out.println("Final count: " + counter.getCount());
}
}
Output:
Final count: 2000
Explanation:
- The
Counter
class has a synchronizedincrement
method. - Two threads are created and both execute the
increment
method 1000 times each. - The final count is 2000, demonstrating that the synchronization prevents race conditions.
6. Example: Synchronized Block
Example:
class Counter {
private int count = 0;
public void increment() {
synchronized (this) {
count++;
}
}
public int getCount() {
return count;
}
}
public class SynchronizedBlockExample {
public static void main(String[] args) throws InterruptedException {
Counter counter = new Counter();
Runnable task = () -> {
for (int i = 0; i < 1000; i++) {
counter.increment();
}
};
Thread thread1 = new Thread(task);
Thread thread2 = new Thread(task);
thread1.start();
thread2.start();
thread1.join();
thread2.join();
System.out.println("Final count: " + counter.getCount());
}
}
Output:
Final count: 2000
Explanation:
- The
Counter
class has anincrement
method with a synchronized block. - Two threads are created and both execute the
increment
method 1000 times each. - The final count is 2000, demonstrating that the synchronization prevents race conditions.
7. Reentrant Synchronization
In Java, the locks used in synchronized methods and blocks are reentrant. This means that if a thread already holds a lock on a synchronized method or block, it can re-enter any synchronized method or block on the same object.
Example:
class ReentrantExample {
public synchronized void method1() {
System.out.println("Inside method1");
method2();
}
public synchronized void method2() {
System.out.println("Inside method2");
}
public static void main(String[] args) {
ReentrantExample example = new ReentrantExample();
example.method1();
}
}
Output:
Inside method1
Inside method2
Explanation:
- The
method1
callsmethod2
, both of which are synchronized. - The thread re-enters the lock it already holds, demonstrating reentrant synchronization.
8. Synchronization and Performance
While synchronization is essential for thread safety, it can impact performance due to the overhead of acquiring and releasing locks. To reduce contention and improve performance, it's crucial to minimize the scope of synchronized blocks and methods.
9. Conclusion
Synchronization in Java is used to ensure thread safety and prevent race conditions in a multithreaded environment. By using synchronized methods, synchronized blocks, and static synchronization, you can control the access of multiple threads to shared resources. However, it's important to use synchronization judiciously to balance thread safety and performance.
Happy coding!
Comments
Post a Comment
Leave Comment