Multithreading: Synchronization in Java
In multithreading, synchronization is used to control the access of multiple threads to shared resources. Without synchronization, inconsistent data can occur when two or more threads access shared resources simultaneously. This tutorial explains how to use synchronization in Java with examples.
Step 1: The Problem Without Synchronization
Consider an example where multiple threads are incrementing a shared counter without synchronization:
class Counter { private int count = 0; public void increment() { count++; } public int getCount() { return count; } } public class NoSynchronizationExample { public static void main(String[] args) { Counter counter = new Counter(); Runnable task = () -> { for (int i = 0; i < 1000; i++) { counter.increment(); } }; Thread t1 = new Thread(task); Thread t2 = new Thread(task); t1.start(); t2.start(); try { t1.join(); t2.join(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Final count: " + counter.getCount()); // May produce incorrect result } }
Step 2: Using Synchronized Methods
You can synchronize a method by using the synchronized
keyword. Below is the corrected version:
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) { Counter counter = new Counter(); Runnable task = () -> { for (int i = 0; i < 1000; i++) { counter.increment(); } }; Thread t1 = new Thread(task); Thread t2 = new Thread(task); t1.start(); t2.start(); try { t1.join(); t2.join(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Final count: " + counter.getCount()); // Correct result } }
Step 3: Using Synchronized Blocks
Instead of synchronizing the entire method, you can synchronize only the critical section of code:
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) { Counter counter = new Counter(); Runnable task = () -> { for (int i = 0; i < 1000; i++) { counter.increment(); } }; Thread t1 = new Thread(task); Thread t2 = new Thread(task); t1.start(); t2.start(); try { t1.join(); t2.join(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Final count: " + counter.getCount()); } }
Step 4: Synchronizing Static Methods
If a method is static, you can synchronize it using the class object:
class StaticCounter { private static int count = 0; public static synchronized void increment() { count++; } public static int getCount() { return count; } } public class SynchronizedStaticMethodExample { public static void main(String[] args) { Runnable task = () -> { for (int i = 0; i < 1000; i++) { StaticCounter.increment(); } }; Thread t1 = new Thread(task); Thread t2 = new Thread(task); t1.start(); t2.start(); try { t1.join(); t2.join(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Final count: " + StaticCounter.getCount()); } }
Summary
In this tutorial, you learned:
- The problem with shared resources in multithreading
- How to use synchronized methods to prevent data inconsistency
- How to use synchronized blocks for finer control
- How to synchronize static methods
Synchronization is essential for ensuring thread safety when working with shared resources in Java.