google-site-verification: google61fe8ba583a51912.html
ReadWriteLock for Concurrent Access in Java

ReadWriteLock is a sophisticated locking mechanism that allows multiple threads to read a shared resource simultaneously, while ensuring exclusive access for write operations. This provides better performance in read-heavy scenarios compared to traditional synchronized blocks or ReentrantLock.

1. ReadWriteLock Fundamentals

What is ReadWriteLock?

  • Multiple readers can access the resource concurrently
  • Single writer gets exclusive access (no readers or other writers)
  • Write-preferring or Read-preferring policies available
  • Improves performance for read-heavy workloads

Key Components

  • ReentrantReadWriteLock - Main implementation
  • ReadLock - Shared lock for read operations
  • WriteLock - Exclusive lock for write operations

2. Basic ReadWriteLock Usage

Simple ReadWriteLock Example

import java.util.concurrent.locks.*; public class BasicReadWriteLockExample { private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); private final ReadWriteLock.ReadLock readLock = lock.readLock(); private final ReadWriteLock.WriteLock writeLock = lock.writeLock(); private String sharedData = "Initial Data"; private int readCount = 0; private int writeCount = 0; public String readData() { readLock.lock(); try { // Multiple threads can read simultaneously readCount++; System.out.println(Thread.currentThread().getName() + " reading data: " + sharedData); Thread.sleep(100); // Simulate read operation return sharedData; } catch (InterruptedException e) { Thread.currentThread().interrupt(); return null; } finally { readLock.unlock(); } } public void writeData(String newData) { writeLock.lock(); try { // Only one thread can write at a time writeCount++; System.out.println(Thread.currentThread().getName() + " writing data: " + newData); Thread.sleep(200); // Simulate write operation sharedData = newData; } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { writeLock.unlock(); } } public void printStats() { readLock.lock(); try { System.out.println("Read operations: " + readCount + ", Write operations: " + writeCount); } finally { readLock.unlock(); } } public static void main(String[] args) throws InterruptedException { BasicReadWriteLockExample example = new BasicReadWriteLockExample(); // Create multiple reader threads Thread[] readers = new Thread[5]; for (int i = 0; i < readers.length; i++) { readers[i] = new Thread(() -> { for (int j = 0; j < 3; j++) { example.readData(); } }, "Reader-" + (i + 1)); } // Create writer threads Thread[] writers = new Thread[2]; for (int i = 0; i < writers.length; i++) { writers[i] = new Thread(() -> { for (int j = 0; j < 2; j++) { example.writeData("Data from " + Thread.currentThread().getName()); } }, "Writer-" + (i + 1)); } // Start all threads for (Thread reader : readers) reader.start(); for (Thread writer : writers) writer.start(); // Wait for completion for (Thread reader : readers) reader.join(); for (Thread writer : writers) writer.join(); example.printStats(); } }

TryLock with ReadWriteLock

import java.util.concurrent.locks.*; import java.util.concurrent.TimeUnit; public class TryLockExample { private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); private final Lock readLock = lock.readLock(); private final Lock writeLock = lock.writeLock(); private String data = "Initial"; private boolean resourceAvailable = true; public String tryReadData(long timeout, TimeUnit unit) { try { if (readLock.tryLock(timeout, unit)) { try { System.out.println(Thread.currentThread().getName() + " acquired read lock"); Thread.sleep(100); return data; } finally { readLock.unlock(); } } else { System.out.println(Thread.currentThread().getName() + " could not acquire read lock"); return "Read timeout"; } } catch (InterruptedException e) { Thread.currentThread().interrupt(); return "Interrupted"; } } public boolean tryWriteData(String newData, long timeout, TimeUnit unit) { try { if (writeLock.tryLock(timeout, unit)) { try { System.out.println(Thread.currentThread().getName() + " acquired write lock"); Thread.sleep(200); data = newData; return true; } finally { writeLock.unlock(); } } else { System.out.println(Thread.currentThread().getName() + " could not acquire write lock"); return false; } } catch (InterruptedException e) { Thread.currentThread().interrupt(); return false; } } public static void main(String[] args) throws InterruptedException { TryLockExample example = new TryLockExample(); // Thread that will hold write lock for a long time Thread longWriter = new Thread(() -> { example.writeLock.lock(); try { System.out.println("Long writer holding lock..."); Thread.sleep(3000); // Hold lock for 3 seconds example.data = "Updated by long writer"; } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { example.writeLock.unlock(); } }, "LongWriter"); // Threads that will try to acquire locks with timeout Thread tryingReader = new Thread(() -> { String result = example.tryReadData(1, TimeUnit.SECONDS); System.out.println("Trying reader result: " + result); }, "TryingReader"); Thread tryingWriter = new Thread(() -> { boolean success = example.tryWriteData("New data", 1, TimeUnit.SECONDS); System.out.println("Trying writer success: " + success); }, "TryingWriter"); longWriter.start(); Thread.sleep(100); // Ensure long writer gets lock first tryingReader.start(); tryingWriter.start(); longWriter.join(); tryingReader.join(); tryingWriter.join(); } }

3. Real-World Use Cases

Use Case 1: Thread-Safe Cache with ReadWriteLock

import java.util.concurrent.locks.*; import java.util.*; public class ReadWriteLockCache<K, V> { private final Map<K, V> cache = new HashMap<>(); private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); private final Lock readLock = lock.readLock(); private final Lock writeLock = lock.writeLock(); private int hitCount = 0; private int missCount = 0; public V get(K key) { readLock.lock(); try { V value = cache.get(key); if (value != null) { hitCount++; return value; } } finally { readLock.unlock(); } // Cache miss - compute value (without holding read lock) V computedValue = computeValue(key); writeLock.lock(); try { // Double-check after acquiring write lock V existingValue = cache.get(key); if (existingValue != null) { // Another thread already computed the value return existingValue; } cache.put(key, computedValue); missCount++; return computedValue; } finally { writeLock.unlock(); } } public void put(K key, V value) { writeLock.lock(); try { cache.put(key, value); } finally { writeLock.unlock(); } } public boolean containsKey(K key) { readLock.lock(); try { return cache.containsKey(key); } finally { readLock.unlock(); } } public V remove(K key) { writeLock.lock(); try { return cache.remove(key); } finally { writeLock.unlock(); } } public void clear() { writeLock.lock(); try { cache.clear(); hitCount = 0; missCount = 0; } finally { writeLock.unlock(); } } public int size() { readLock.lock(); try { return cache.size(); } finally { readLock.unlock(); } } public Map<K, V> snapshot() { readLock.lock(); try { return new HashMap<>(cache); } finally { readLock.unlock(); } } public CacheStats getStats() { readLock.lock(); try { return new CacheStats(hitCount, missCount, cache.size()); } finally { readLock.unlock(); } } @SuppressWarnings("unchecked") private V computeValue(K key) { // Simulate expensive computation try { Thread.sleep(100); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } return (V) ("ComputedValueFor_" + key); } public static class CacheStats { private final int hitCount; private final int missCount; private final int size; public CacheStats(int hitCount, int missCount, int size) { this.hitCount = hitCount; this.missCount = missCount; this.size = size; } public double hitRatio() { long total = hitCount + missCount; return total == 0 ? 0.0 : (double) hitCount / total; } @Override public String toString() { return String.format("CacheStats{size=%d, hits=%d, misses=%d, hitRatio=%.2f}", size, hitCount, missCount, hitRatio()); } } public static void main(String[] args) throws InterruptedException { ReadWriteLockCache<String, String> cache = new ReadWriteLockCache<>(); int threadCount = 10; int operationsPerThread = 100; Thread[] threads = new Thread[threadCount]; for (int i = 0; i < threadCount; i++) { final int threadId = i; threads[i] = new Thread(() -> { Random random = new Random(); for (int j = 0; j < operationsPerThread; j++) { String key = "key-" + random.nextInt(50); // Limited key space for cache hits if (random.nextDouble() < 0.8) { // 80% reads cache.get(key); } else { // 20% writes cache.put(key, "value-from-thread-" + threadId); } } }); } for (Thread thread : threads) thread.start(); for (Thread thread : threads) thread.join(); System.out.println("Final cache stats: " + cache.getStats()); System.out.println("Cache contents: " + cache.snapshot()); } }

Use Case 2: Configuration Manager with ReadWriteLock

import java.util.concurrent.locks.*; import java.util.*; import java.util.concurrent.TimeUnit; public class ConfigurationManager { private final Properties config = new Properties(); private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true); // Fair lock private final Lock readLock = lock.readLock(); private final Lock writeLock = lock.writeLock(); private final List<ConfigListener> listeners = new ArrayList<>(); private volatile long lastModified; public ConfigurationManager() { // Initial configuration config.setProperty("database.url", "jdbc:mysql://localhost:3306/mydb"); config.setProperty("database.username", "admin"); config.setProperty("database.password", "secret"); config.setProperty("cache.size", "1000"); config.setProperty("thread.pool.size", "10"); lastModified = System.currentTimeMillis(); } public String getConfig(String key) { readLock.lock(); try { return config.getProperty(key); } finally { readLock.unlock(); } } public String getConfig(String key, String defaultValue) { readLock.lock(); try { return config.getProperty(key, defaultValue); } finally { readLock.unlock(); } } public Properties getAllConfig() { readLock.lock(); try { return new Properties(config); // Return copy } finally { readLock.unlock(); } } public void setConfig(String key, String value) { writeLock.lock(); try { String oldValue = config.getProperty(key); config.setProperty(key, value); lastModified = System.currentTimeMillis(); // Notify listeners if (!Objects.equals(oldValue, value)) { notifyListeners(key, oldValue, value); } } finally { writeLock.unlock(); } } public void bulkUpdate(Map<String, String> updates) { writeLock.lock(); try { Map<String, String> changes = new HashMap<>(); for (Map.Entry<String, String> entry : updates.entrySet()) { String oldValue = config.getProperty(entry.getKey()); config.setProperty(entry.getKey(), entry.getValue()); if (!Objects.equals(oldValue, entry.getValue())) { changes.put(entry.getKey(), oldValue); } } lastModified = System.currentTimeMillis(); // Notify listeners for each change for (Map.Entry<String, String> change : changes.entrySet()) { notifyListeners(change.getKey(), change.getValue(), updates.get(change.getKey())); } } finally { writeLock.unlock(); } } public boolean reloadFromExternalSource(Properties newConfig) { // Try to acquire write lock with timeout to avoid blocking during reload try { if (writeLock.tryLock(5, TimeUnit.SECONDS)) { try { Map<String, String> changes = findChanges(newConfig); config.clear(); config.putAll(newConfig); lastModified = System.currentTimeMillis(); // Notify listeners of all changes for (Map.Entry<String, String> change : changes.entrySet()) { notifyListeners(change.getKey(), change.getValue(), newConfig.getProperty(change.getKey())); } return true; } finally { writeLock.unlock(); } } else { System.out.println("Could not acquire write lock for reload - configuration busy"); return false; } } catch (InterruptedException e) { Thread.currentThread().interrupt(); return false; } } private Map<String, String> findChanges(Properties newConfig) { Map<String, String> changes = new HashMap<>(); // Check for modified or removed properties for (String key : config.stringPropertyNames()) { String oldValue = config.getProperty(key); String newValue = newConfig.getProperty(key); if (!Objects.equals(oldValue, newValue)) { changes.put(key, oldValue); } } // Check for new properties for (String key : newConfig.stringPropertyNames()) { if (!config.containsKey(key)) { changes.put(key, null); } } return changes; } // Listener management public void addListener(ConfigListener listener) { writeLock.lock(); try { listeners.add(listener); } finally { writeLock.unlock(); } } public void removeListener(ConfigListener listener) { writeLock.lock(); try { listeners.remove(listener); } finally { writeLock.unlock(); } } private void notifyListeners(String key, String oldValue, String newValue) { // Use read lock for notification to allow concurrent reads during notifications readLock.lock(); try { List<ConfigListener> currentListeners = new ArrayList<>(listeners); readLock.unlock(); // Release read lock before calling external code for (ConfigListener listener : currentListeners) { try { listener.onConfigChanged(key, oldValue, newValue); } catch (Exception e) { System.err.println("Error in config listener: " + e.getMessage()); } } } finally { // If we still hold the read lock, release it if (lock.getReadHoldCount() > 0) { readLock.unlock(); } } } public long getLastModified() { // No lock needed for volatile read return lastModified; } public interface ConfigListener { void onConfigChanged(String key, String oldValue, String newValue); } // Example usage public static void main(String[] args) throws InterruptedException { ConfigurationManager configManager = new ConfigurationManager(); // Add a listener configManager.addListener((key, oldValue, newValue) -> { System.out.printf("Config changed: %s = %s -> %s%n", key, oldValue, newValue); }); // Multiple reader threads Thread[] readers = new Thread[5]; for (int i = 0; i < readers.length; i++) { readers[i] = new Thread(() -> { Random random = new Random(); for (int j = 0; j < 10; j++) { String value = configManager.getConfig("database.url"); System.out.println(Thread.currentThread().getName() + " read: " + value); try { Thread.sleep(random.nextInt(100)); } catch (InterruptedException e) { Thread.currentThread().interrupt(); break; } } }, "Reader-" + i); } // Writer thread Thread writer = new Thread(() -> { for (int i = 0; i < 3; i++) { configManager.setConfig("cache.size", String.valueOf(1000 + i * 100)); try { Thread.sleep(500); } catch (InterruptedException e) { Thread.currentThread().interrupt(); break; } } }, "Writer"); // Start all threads for (Thread reader : readers) reader.start(); writer.start(); // Wait for completion for (Thread reader : readers) reader.join(); writer.join(); System.out.println("Final configuration: " + configManager.getAllConfig()); } }

4. Advanced ReadWriteLock Patterns

Lock Downgrading Pattern

import java.util.concurrent.locks.*; public class LockDowngradingExample { private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); private final Lock readLock = lock.readLock(); private final Lock writeLock = lock.writeLock(); private volatile String data; private volatile boolean initialized = false; public void initializeIfNeeded() { // First, try with read lock (fast path) if (initialized) { return; } // Not initialized, acquire write lock writeLock.lock(); try { // Double-check inside write lock if (!initialized) { System.out.println(Thread.currentThread().getName() + " initializing data..."); // Simulate expensive initialization Thread.sleep(1000); data = "Initialized Data"; initialized = true; // Downgrade to read lock before performing read-heavy operations readLock.lock(); try { writeLock.unlock(); // Release write lock but keep read lock // Now we have read lock, other readers can proceed performReadOperations(); } finally { readLock.unlock(); } } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { // Ensure write lock is released if we still hold it if (lock.isWriteLockedByCurrentThread()) { writeLock.unlock(); } } } private void performReadOperations() { // This method can be called by multiple threads concurrently System.out.println(Thread.currentThread().getName() + " performing read operations on: " + data); // Simulate read-heavy work try { Thread.sleep(500); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } public String getData() { readLock.lock(); try { return data; } finally { readLock.unlock(); } } public static void main(String[] args) throws InterruptedException { LockDowngradingExample example = new LockDowngradingExample(); Thread[] threads = new Thread[5]; for (int i = 0; i < threads.length; i++) { threads[i] = new Thread(() -> { example.initializeIfNeeded(); System.out.println(Thread.currentThread().getName() + " got data: " + example.getData()); }, "Thread-" + i); } for (Thread thread : threads) thread.start(); for (Thread thread : threads) thread.join(); } }

ReadWriteLock with Condition

import java.util.concurrent.locks.*; import java.util.*; public class ReadWriteLockWithCondition { private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); private final Lock readLock = lock.readLock(); private final Lock writeLock = lock.writeLock(); private final Condition dataAvailable = writeLock.newCondition(); private final Condition spaceAvailable = writeLock.newCondition(); private final Queue<String> queue = new LinkedList<>(); private final int maxSize = 10; private boolean shutdown = false; public void produce(String item) throws InterruptedException { writeLock.lock(); try { while (queue.size() == maxSize && !shutdown) { System.out.println(Thread.currentThread().getName() + " waiting for space..."); spaceAvailable.await(); } if (shutdown) { System.out.println("Producer shutting down"); return; } queue.offer(item); System.out.println(Thread.currentThread().getName() + " produced: " + item); dataAvailable.signalAll(); // Notify all consumers } finally { writeLock.unlock(); } } public String consume() throws InterruptedException { writeLock.lock(); try { while (queue.isEmpty() && !shutdown) { System.out.println(Thread.currentThread().getName() + " waiting for data..."); dataAvailable.await(); } if (shutdown && queue.isEmpty()) { return null; // Shutdown signal } String item = queue.poll(); System.out.println(Thread.currentThread().getName() + " consumed: " + item); spaceAvailable.signalAll(); // Notify all producers return item; } finally { writeLock.unlock(); } } public List<String> snapshot() { readLock.lock(); try { return new ArrayList<>(queue); } finally { readLock.unlock(); } } public int size() { readLock.lock(); try { return queue.size(); } finally { readLock.unlock(); } } public void shutdown() { writeLock.lock(); try { shutdown = true; dataAvailable.signalAll(); // Wake up all waiting consumers spaceAvailable.signalAll(); // Wake up all waiting producers } finally { writeLock.unlock(); } } public static void main(String[] args) throws InterruptedException { ReadWriteLockWithCondition example = new ReadWriteLockWithCondition(); // Producers Thread[] producers = new Thread[3]; for (int i = 0; i < producers.length; i++) { final int producerId = i; producers[i] = new Thread(() -> { try { for (int j = 0; j < 5; j++) { example.produce("item-" + producerId + "-" + j); Thread.sleep(100); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } }, "Producer-" + i); } // Consumers Thread[] consumers = new Thread[2]; for (int i = 0; i < consumers.length; i++) { consumers[i] = new Thread(() -> { try { while (true) { String item = example.consume(); if (item == null) break; // Shutdown signal Thread.sleep(150); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } }, "Consumer-" + i); } // Monitor thread Thread monitor = new Thread(() -> { try { for (int i = 0; i < 10; i++) { System.out.println("Queue snapshot: " + example.snapshot() + " (size: " + example.size() + ")"); Thread.sleep(200); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } }, "Monitor"); // Start all threads for (Thread producer : producers) producer.start(); for (Thread consumer : consumers) consumer.start(); monitor.start(); // Wait for producers to finish for (Thread producer : producers) producer.join(); // Shutdown and wait for consumers Thread.sleep(1000); example.shutdown(); for (Thread consumer : consumers) consumer.join(); monitor.join(); System.out.println("Final queue: " + example.snapshot()); } }

5. Performance Considerations

ReadWriteLock vs Synchronized Benchmark

import java.util.concurrent.locks.*; import java.util.concurrent.*; public class ReadWriteLockBenchmark { private final Object syncLock = new Object(); private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock(); private final Lock readLock = rwLock.readLock(); private final Lock writeLock = rwLock.writeLock(); private int value; private int readCount = 0; private int writeCount = 0; // Synchronized version public int readWithSynchronized() { synchronized (syncLock) { readCount++; return value; } } public void writeWithSynchronized(int newValue) { synchronized (syncLock) { writeCount++; value = newValue; } } // ReadWriteLock version public int readWithReadWriteLock() { readLock.lock(); try { readCount++; return value; } finally { readLock.unlock(); } } public void writeWithReadWriteLock(int newValue) { writeLock.lock(); try { writeCount++; value = newValue; } finally { writeLock.unlock(); } } public void reset() { synchronized (syncLock) { readCount = 0; writeCount = 0; value = 0; } } public static void main(String[] args) throws InterruptedException { ReadWriteLockBenchmark benchmark = new ReadWriteLockBenchmark(); int threadCount = 10; int operationsPerThread = 10000; double readRatio = 0.9; // 90% reads, 10% writes System.out.println("Benchmarking with " + threadCount + " threads, " + operationsPerThread + " operations per thread, " + (readRatio * 100) + "% reads"); // Test synchronized long syncTime = runBenchmark(benchmark, threadCount, operationsPerThread, readRatio, true); int syncReads = benchmark.readCount; int syncWrites = benchmark.writeCount; benchmark.reset(); // Test ReadWriteLock long rwTime = runBenchmark(benchmark, threadCount, operationsPerThread, readRatio, false); int rwReads = benchmark.readCount; int rwWrites = benchmark.writeCount; System.out.println("\nResults:"); System.out.printf("Synchronized: %d ms (reads: %d, writes: %d)%n", syncTime, syncReads, syncWrites); System.out.printf("ReadWriteLock: %d ms (reads: %d, writes: %d)%n", rwTime, rwReads, rwWrites); System.out.printf("Improvement: %.2fx%n", (double) syncTime / rwTime); } private static long runBenchmark(ReadWriteLockBenchmark benchmark, int threadCount, int operationsPerThread, double readRatio, boolean useSynchronized) throws InterruptedException { CountDownLatch startLatch = new CountDownLatch(1); CountDownLatch endLatch = new CountDownLatch(threadCount); Thread[] threads = new Thread[threadCount]; for (int i = 0; i < threadCount; i++) { threads[i] = new Thread(() -> { try { startLatch.await(); // Wait for start signal Random random = new Random(); for (int j = 0; j < operationsPerThread; j++) { if (random.nextDouble() < readRatio) { if (useSynchronized) { benchmark.readWithSynchronized(); } else { benchmark.readWithReadWriteLock(); } } else { if (useSynchronized) { benchmark.writeWithSynchronized(random.nextInt()); } else { benchmark.writeWithReadWriteLock(random.nextInt()); } } } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { endLatch.countDown(); } }); } for (Thread thread : threads) thread.start(); long startTime = System.currentTimeMillis(); startLatch.countDown(); // Start all threads endLatch.await(); // Wait for all threads to complete long endTime = System.currentTimeMillis(); return endTime - startTime; } }

6. Best Practices and Common Pitfalls

Best Practices Example

import java.util.concurrent.locks.*; public class ReadWriteLockBestPractices { private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); private final Lock readLock = lock.readLock(); private final Lock writeLock = lock.writeLock(); private String data; // ✅ GOOD: Always use try-finally public String safeRead() { readLock.lock(); try { return data; } finally { readLock.unlock(); } } // ✅ GOOD: Use tryLock with timeout for write operations public boolean safeWrite(String newData, long timeout, TimeUnit unit) { try { if (writeLock.tryLock(timeout, unit)) { try { data = newData; return true; } finally { writeLock.unlock(); } } return false; } catch (InterruptedException e) { Thread.currentThread().interrupt(); return false; } } // ✅ GOOD: Document lock ordering if using multiple locks public void multipleResourceAccess(String otherData, ReadWriteLockBestPractices other) { // Always acquire locks in consistent order to prevent deadlocks boolean locked1 = false; boolean locked2 = false; try { if (this.writeLock.tryLock(100, TimeUnit.MILLISECONDS)) { locked1 = true; if (other.writeLock.tryLock(100, TimeUnit.MILLISECONDS)) { locked2 = true; // Both locks acquired - perform operation this.data = otherData; other.data = this.data; } } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { if (locked2) other.writeLock.unlock(); if (locked1) this.writeLock.unlock(); } } // ❌ BAD: Don't forget to unlock in all code paths public String unsafeRead(boolean shouldProcess) { readLock.lock(); if (shouldProcess) { // ❌ If shouldProcess is false, lock is never released! return data.toUpperCase(); } readLock.unlock(); // ❌ This might not be reached return data; } // ❌ BAD: Don't call external code while holding locks public void unsafeOperation(String newData) { writeLock.lock(); try { data = newData; externalService.call(data); // ❌ External call while holding lock } finally { writeLock.unlock(); } } // ✅ GOOD: Release lock before calling external code public void safeOperation(String newData) { String dataToSend; writeLock.lock(); try { data = newData; dataToSend = data; } finally { writeLock.unlock(); } // Call external service without holding the lock externalService.call(dataToSend); } // Mock external service for demonstration private static class ExternalService { void call(String data) { // Simulate external service call try { Thread.sleep(100); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } } private final ExternalService externalService = new ExternalService(); }

Summary

ReadWriteLock provides significant advantages:

  • Performance Boost - Multiple concurrent readers
  • Write Exclusion - Single writer with exclusive access
  • Flexibility - TryLock, timed operations, conditions
  • Fairness Options - Fair or non-fair locking

Key patterns:

  • Cache implementations - Read-heavy with occasional updates
  • Configuration management - Frequent reads, rare writes
  • Resource pooling - Shared resource access
  • Data structures - Thread-safe collections

Best practices:

  • Always use try-finally for lock/unlock
  • Use tryLock with timeouts to avoid deadlocks
  • Release locks before calling external code
  • Consider lock downgrading for read-heavy post-processing
  • Use fair locking when thread starvation is a concern

ReadWriteLock is ideal for read-heavy workloads where data is read frequently but updated infrequently, providing better scalability than synchronized methods or ReentrantLock in these scenarios.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper