Java Collections.synchronizedList Method
Last modified: April 20, 2025
The Collections.synchronizedList
method provides thread-safe
access to List implementations. It returns a synchronized (thread-safe) list
backed by the specified list. All operations are synchronized, preventing
concurrent modification issues.
This method is part of Java's Collections Framework utility class. It wraps existing List implementations to make them thread-safe. The returned list must be manually synchronized for iteration.
Collections.synchronizedList Overview
The synchronizedList
method creates a thread-safe wrapper around
a List. It synchronizes all individual operations like add, remove, and get.
This prevents data corruption in multi-threaded environments.
While individual operations are thread-safe, compound operations require external synchronization. Iteration also needs manual synchronization to prevent ConcurrentModificationException. The method is declared in the Collections utility class.
Basic synchronizedList Example
This example demonstrates creating a synchronized list from an ArrayList. We perform basic operations to show thread-safe access. The example shows how to create and use a synchronized list.
package com.zetcode; import java.util.ArrayList; import java.util.Collections; import java.util.List; public class BasicSynchronizedList { public static void main(String[] args) { // Create regular ArrayList List<String> normalList = new ArrayList<>(); // Create synchronized list List<String> syncList = Collections.synchronizedList(normalList); // Add elements - thread-safe syncList.add("Apple"); syncList.add("Banana"); syncList.add("Cherry"); // Remove element - thread-safe syncList.remove("Banana"); System.out.println("Synchronized list: " + syncList); } }
This code shows basic usage of Collections.synchronizedList
. We
create a regular ArrayList, then wrap it in a synchronized list. All operations
on syncList are thread-safe.
The output demonstrates that the synchronized list behaves like a normal list but with thread safety. Individual operations like add and remove are atomic.
Multi-thread Access Example
This example demonstrates thread-safe access to a synchronized list from multiple threads. We create several threads that concurrently modify the list. The synchronized wrapper prevents data corruption.
package com.zetcode; import java.util.ArrayList; import java.util.Collections; import java.util.List; public class MultiThreadSynchronizedList { public static void main(String[] args) throws InterruptedException { List<Integer> numbers = Collections.synchronizedList(new ArrayList<>()); // Create and start multiple threads Thread[] threads = new Thread[5]; for (int i = 0; i < threads.length; i++) { final int threadId = i; threads[i] = new Thread(() -> { for (int j = 0; j < 100; j++) { numbers.add(threadId * 100 + j); } }); threads[i].start(); } // Wait for all threads to complete for (Thread t : threads) { t.join(); } System.out.println("Total elements: " + numbers.size()); } }
This example shows how multiple threads can safely access a synchronized list. Each thread adds 100 elements to the shared list. Without synchronization, this would cause data corruption.
The output shows the correct total count of 500 elements (5 threads × 100 elements each). The synchronized list ensures thread-safe access during concurrent modification.
Iterating a Synchronized List
Iterating a synchronized list requires manual synchronization. While individual operations are thread-safe, iteration involves multiple operations. This example shows the correct way to iterate.
package com.zetcode; import java.util.ArrayList; import java.util.Collections; import java.util.List; public class SynchronizedListIteration { public static void main(String[] args) { List<String> syncList = Collections.synchronizedList(new ArrayList<>()); syncList.add("Red"); syncList.add("Green"); syncList.add("Blue"); // Proper synchronized iteration synchronized(syncList) { for (String color : syncList) { System.out.println(color); } } // Unsafe iteration (may throw ConcurrentModificationException) // for (String color : syncList) { // System.out.println(color); // } } }
This example demonstrates the correct way to iterate a synchronized list. The entire iteration block is wrapped in a synchronized block using the list as the lock.
The commented-out unsafe iteration shows what not to do. Without explicit synchronization, concurrent modifications during iteration could cause exceptions. Always synchronize iteration manually.
Compound Operations Example
Compound operations on synchronized lists require external synchronization. This example shows how to safely perform operations that involve multiple method calls. The "check-then-act" pattern is demonstrated.
package com.zetcode; import java.util.ArrayList; import java.util.Collections; import java.util.List; public class CompoundOperations { public static void main(String[] args) { List<Integer> syncList = Collections.synchronizedList(new ArrayList<>()); // Unsafe compound operation if (!syncList.contains(42)) { syncList.add(42); // Race condition possible } // Safe compound operation synchronized(syncList) { if (!syncList.contains(42)) { syncList.add(42); } } System.out.println("List contains 42: " + syncList.contains(42)); } }
This example highlights the need for external synchronization with compound operations. The unsafe version shows a potential race condition between contains and add calls.
The safe version wraps both operations in a synchronized block. This ensures atomic execution of the compound operation. Always synchronize multi-step operations manually.
Performance Comparison
This example compares the performance of synchronized lists versus regular lists. Synchronization adds overhead but provides thread safety. The test measures the performance impact.
package com.zetcode; import java.util.ArrayList; import java.util.Collections; import java.util.List; public class PerformanceComparison { public static void main(String[] args) { final int ITERATIONS = 1000000; List<Integer> normalList = new ArrayList<>(); List<Integer> syncList = Collections.synchronizedList(new ArrayList<>()); // Test normal list long start = System.currentTimeMillis(); for (int i = 0; i < ITERATIONS; i++) { normalList.add(i); } long normalTime = System.currentTimeMillis() - start; // Test synchronized list start = System.currentTimeMillis(); for (int i = 0; i < ITERATIONS; i++) { syncList.add(i); } long syncTime = System.currentTimeMillis() - start; System.out.println("Normal list time: " + normalTime + "ms"); System.out.println("Sync list time: " + syncTime + "ms"); System.out.println("Overhead: " + (syncTime - normalTime) + "ms"); } }
This performance test compares synchronized and unsynchronized list operations. The synchronized version shows measurable overhead due to locking. The exact difference depends on system and JVM characteristics.
The output demonstrates the trade-off between thread safety and performance. Use synchronized lists only when thread safety is required. Consider other options like CopyOnWriteArrayList for specific use cases.
Alternative to synchronizedList
This example shows CopyOnWriteArrayList as an alternative to synchronizedList. It provides thread safety without explicit synchronization. The trade-offs between these approaches are discussed.
package com.zetcode; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; public class CopyOnWriteExample { public static void main(String[] args) { List<String> cowList = new CopyOnWriteArrayList<>(); cowList.add("Java"); cowList.add("Python"); cowList.add("C++"); // Safe iteration without synchronization for (String lang : cowList) { System.out.println(lang); cowList.add("JavaScript"); // Modification during iteration } System.out.println("Final list: " + cowList); } }
CopyOnWriteArrayList provides thread safety through a different mechanism. It creates a new copy of the underlying array on modification. This allows safe iteration without synchronization.
The example shows modification during iteration, which would fail with a synchronized list. The trade-off is higher memory usage and write overhead. Choose based on your specific requirements.
Real-world Use Case
This example demonstrates a real-world scenario using synchronizedList. We simulate a logging system where multiple threads add log entries. The synchronized list ensures thread-safe logging.
package com.zetcode; import java.util.Collections; import java.util.List; import java.util.ArrayList; public class LoggingSystem { private final List<String> logEntries; public LoggingSystem() { this.logEntries = Collections.synchronizedList(new ArrayList<>()); } public void addLogEntry(String entry) { logEntries.add(entry); } public void processLogs() { synchronized(logEntries) { for (String entry : logEntries) { System.out.println(entry); } logEntries.clear(); } } public static void main(String[] args) throws InterruptedException { LoggingSystem logger = new LoggingSystem(); // Simulate multiple threads logging Thread[] threads = new Thread[3]; for (int i = 0; i < threads.length; i++) { final int threadId = i; threads[i] = new Thread(() -> { for (int j = 0; j < 5; j++) { logger.addLogEntry("Thread " + threadId + " - Message " + j); } }); threads[i].start(); } // Wait for threads to finish for (Thread t : threads) { t.join(); } // Process logs logger.processLogs(); } }
This example shows a practical use of synchronizedList in a logging system. Multiple threads can safely add log entries concurrently. The processLogs method demonstrates proper synchronized iteration and clearing.
The output shows all log entries from different threads interleaved but without corruption. This pattern is common in real-world concurrent applications.
Source
Java Collections.synchronizedList Documentation
In this article, we've explored Java's Collections.synchronizedList
method in depth. We've covered basic usage, multi-thread scenarios, iteration,
performance, and alternatives. Understanding thread-safe collections is crucial
for concurrent programming.
Author
List all Java tutorials.