Əsas məzmuna keçin

Java ReadWriteLock

ReadWriteLock Nədir?

ReadWriteLock oxuma və yazma əməliyyatları üçün ayrı-ayrı lock-lar təqdim edən synchronization mexanizmidir. Bir neçə thread eyni vaxtda oxuya bilər, amma yazma zamanı eksklüziv lock alınır.

Xüsusiyyətlər:

  • Read lock - Bir neçə thread eyni vaxtda oxuya bilər
  • Write lock - Yalnız bir thread yaza bilər (eksklüziv)
  • Çox oxuma, az yazma olan ssenarilərdə performans artımı
  • ReentrantReadWriteLock ən çox istifadə olunan implementasiyadır

Basic Usage

import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

class SharedResource {
private final ReadWriteLock rwLock = new ReentrantReadWriteLock();
private String data = "initial";

// Oxuma metodu - bir neçə thread eyni vaxtda çağıra bilər
public String read() {
rwLock.readLock().lock();
try {
System.out.println(Thread.currentThread().getName() + " oxuyur");
Thread.sleep(1000); // Oxuma əməliyyatı
return data;
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return null;
} finally {
rwLock.readLock().unlock();
}
}

// Yazma metodu - yalnız bir thread çağıra bilər
public void write(String newData) {
rwLock.writeLock().lock();
try {
System.out.println(Thread.currentThread().getName() + " yazır");
Thread.sleep(1000); // Yazma əməliyyatı
data = newData;
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
rwLock.writeLock().unlock();
}
}
}

Cache Implementation

class ThreadSafeCache<K, V> {
private final Map<K, V> cache = new HashMap<>();
private final ReadWriteLock rwLock = new ReentrantReadWriteLock();

public V get(K key) {
rwLock.readLock().lock();
try {
return cache.get(key);
} finally {
rwLock.readLock().unlock();
}
}

public void put(K key, V value) {
rwLock.writeLock().lock();
try {
cache.put(key, value);
} finally {
rwLock.writeLock().unlock();
}
}

public void remove(K key) {
rwLock.writeLock().lock();
try {
cache.remove(key);
} finally {
rwLock.writeLock().unlock();
}
}

public int size() {
rwLock.readLock().lock();
try {
return cache.size();
} finally {
rwLock.readLock().unlock();
}
}

public void clear() {
rwLock.writeLock().lock();
try {
cache.clear();
} finally {
rwLock.writeLock().unlock();
}
}
}

Statistics Tracker

class StatisticsTracker {
private final ReadWriteLock rwLock = new ReentrantReadWriteLock();
private long totalRequests = 0;
private long successfulRequests = 0;
private long failedRequests = 0;

// Oxuma - bir neçə thread eyni vaxtda
public long getTotalRequests() {
rwLock.readLock().lock();
try {
return totalRequests;
} finally {
rwLock.readLock().unlock();
}
}

public double getSuccessRate() {
rwLock.readLock().lock();
try {
if (totalRequests == 0) return 0.0;
return (double) successfulRequests / totalRequests * 100;
} finally {
rwLock.readLock().unlock();
}
}

public Map<String, Long> getStats() {
rwLock.readLock().lock();
try {
Map<String, Long> stats = new HashMap<>();
stats.put("total", totalRequests);
stats.put("successful", successfulRequests);
stats.put("failed", failedRequests);
return stats;
} finally {
rwLock.readLock().unlock();
}
}

// Yazma - eksklüziv
public void recordSuccess() {
rwLock.writeLock().lock();
try {
totalRequests++;
successfulRequests++;
} finally {
rwLock.writeLock().unlock();
}
}

public void recordFailure() {
rwLock.writeLock().lock();
try {
totalRequests++;
failedRequests++;
} finally {
rwLock.writeLock().unlock();
}
}

public void reset() {
rwLock.writeLock().lock();
try {
totalRequests = 0;
successfulRequests = 0;
failedRequests = 0;
} finally {
rwLock.writeLock().unlock();
}
}
}

Lock Downgrading (Lock Upgrade dəstəklənmir)

class LockDowngradingExample {
private final ReadWriteLock rwLock = new ReentrantReadWriteLock();
private String data = "initial";

// Lock downgrading: write -> read
public void updateAndRead() {
rwLock.writeLock().lock();
try {
// Yazma əməliyyatı
data = "updated";

// Write lock tutarkən read lock al (downgrade)
rwLock.readLock().lock();
} finally {
rwLock.writeLock().unlock(); // Write lock burax
}

try {
// İndi read lock var
System.out.println("Data: " + data);
} finally {
rwLock.readLock().unlock();
}
}

// YANLIŞ - Lock upgrade (read -> write) dəstəklənmir!
public void wrongUpgrade() {
rwLock.readLock().lock();
try {
// Oxuma
if (needsUpdate(data)) {
// Read lock-u burax
rwLock.readLock().unlock();

// Write lock al
rwLock.writeLock().lock();
try {
// Yenidən yoxla (double-check)
if (needsUpdate(data)) {
data = "updated";
}

// Downgrade
rwLock.readLock().lock();
} finally {
rwLock.writeLock().unlock();
}

// Read lock yenidən əldə edildi
rwLock.readLock().lock();
}
} finally {
rwLock.readLock().unlock();
}
}

private boolean needsUpdate(String data) {
return data.equals("initial");
}
}

Fair vs Non-Fair

class FairnessExample {
// Non-fair (default) - daha sürətli
private ReadWriteLock nonFairLock = new ReentrantReadWriteLock(false);

// Fair - FIFO sırası, amma daha yavaş
private ReadWriteLock fairLock = new ReentrantReadWriteLock(true);

public void demonstrateFairness() {
ReadWriteLock lock = new ReentrantReadWriteLock(true); // Fair

// 5 reader
for (int i = 1; i <= 5; i++) {
new Thread(() -> {
lock.readLock().lock();
try {
System.out.println(Thread.currentThread().getName() +
" oxuyur");
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
lock.readLock().unlock();
}
}, "Reader-" + i).start();
}

// 1 writer
new Thread(() -> {
lock.writeLock().lock();
try {
System.out.println(Thread.currentThread().getName() +
" yazır");
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
lock.writeLock().unlock();
}
}, "Writer").start();
}
}

tryLock() with ReadWriteLock

class TryLockExample {
private final ReadWriteLock rwLock = new ReentrantReadWriteLock();
private List<String> data = new ArrayList<>();

public String readWithTryLock() {
if (rwLock.readLock().tryLock()) {
try {
if (data.isEmpty()) {
return null;
}
return data.get(0);
} finally {
rwLock.readLock().unlock();
}
} else {
return "Read lock əldə edilmədi";
}
}

public boolean writeWithTimeout(String item) {
try {
if (rwLock.writeLock().tryLock(1, TimeUnit.SECONDS)) {
try {
data.add(item);
return true;
} finally {
rwLock.writeLock().unlock();
}
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return false;
}
}

Configuration Manager

class ConfigurationManager {
private final ReadWriteLock rwLock = new ReentrantReadWriteLock();
private final Map<String, String> config = new HashMap<>();

public String getProperty(String key) {
rwLock.readLock().lock();
try {
return config.get(key);
} finally {
rwLock.readLock().unlock();
}
}

public Map<String, String> getAllProperties() {
rwLock.readLock().lock();
try {
return new HashMap<>(config); // Defensive copy
} finally {
rwLock.readLock().unlock();
}
}

public void setProperty(String key, String value) {
rwLock.writeLock().lock();
try {
config.put(key, value);
System.out.println("Config updated: " + key + " = " + value);
} finally {
rwLock.writeLock().unlock();
}
}

public void loadFromFile(String filename) {
rwLock.writeLock().lock();
try {
config.clear();
// Load from file
System.out.println("Config loaded from: " + filename);
} finally {
rwLock.writeLock().unlock();
}
}
}

Read-Heavy Workload

class ProductCatalog {
private final ReadWriteLock rwLock = new ReentrantReadWriteLock();
private final Map<Long, Product> products = new HashMap<>();

// Çox tez-tez çağırılır (read-heavy)
public Product getProduct(Long id) {
rwLock.readLock().lock();
try {
return products.get(id);
} finally {
rwLock.readLock().unlock();
}
}

public List<Product> getAllProducts() {
rwLock.readLock().lock();
try {
return new ArrayList<>(products.values());
} finally {
rwLock.readLock().unlock();
}
}

public List<Product> searchProducts(String query) {
rwLock.readLock().lock();
try {
return products.values().stream()
.filter(p -> p.getName().contains(query))
.collect(Collectors.toList());
} finally {
rwLock.readLock().unlock();
}
}

// Nadir çağırılır
public void addProduct(Product product) {
rwLock.writeLock().lock();
try {
products.put(product.getId(), product);
} finally {
rwLock.writeLock().unlock();
}
}

public void updateProduct(Product product) {
rwLock.writeLock().lock();
try {
products.put(product.getId(), product);
} finally {
rwLock.writeLock().unlock();
}
}
}

class Product {
private Long id;
private String name;
private double price;

// constructor, getters, setters
public Long getId() { return id; }
public String getName() { return name; }
}

Performance Comparison

class PerformanceTest {
private static final int THREADS = 10;
private static final int OPERATIONS = 10000;

// synchronized istifadə edən versiya
static class SynchronizedCounter {
private int count = 0;

public synchronized void increment() {
count++;
}

public synchronized int get() {
return count;
}
}

// ReadWriteLock istifadə edən versiya
static class RWLockCounter {
private int count = 0;
private final ReadWriteLock lock = new ReentrantReadWriteLock();

public void increment() {
lock.writeLock().lock();
try {
count++;
} finally {
lock.writeLock().unlock();
}
}

public int get() {
lock.readLock().lock();
try {
return count;
} finally {
lock.readLock().unlock();
}
}
}

// Read-heavy workload-da ReadWriteLock daha sürətlidir
}

Best Practices

  1. Read-heavy ssenarilərdə istifadə edin:

    // Çox oxuma, az yazma
    // ReadWriteLock performans artımı verir
    ReadWriteLock lock = new ReentrantReadWriteLock();
  2. Həmişə try-finally istifadə edin:

    lock.readLock().lock();
    try {
    // read operation
    } finally {
    lock.readLock().unlock();
    }
  3. Write lock-u minimuma endirin:

    // Əvvəlcə hesablamaları et
    Data prepared = prepareData();

    // Sonra write lock al
    lock.writeLock().lock();
    try {
    updateSharedState(prepared);
    } finally {
    lock.writeLock().unlock();
    }
  4. Lock upgrade etməyə çalışmayın:

    // YANLIŞ - deadlock riski
    lock.readLock().lock();
    lock.writeLock().lock(); // Block olacaq!

    // DÜZGÜN - read lock burax, write lock al
    lock.readLock().unlock();
    lock.writeLock().lock();
  5. Fair vs non-fair seçin:

    // Non-fair (default) - performans
    new ReentrantReadWriteLock(false);

    // Fair - ədalət lazımsa
    new ReentrantReadWriteLock(true);

Nə Zaman İstifadə Etməli?

ReadWriteLock istifadə edin:

  • Çox oxuma, az yazma olan ssenarilərdə
  • Oxuma əməliyyatları uzun çəkəndə
  • Performance kritikdirsə
  • Cache, configuration, catalog kimi strukturlar

Sadə synchronized istifadə edin:

  • Oxuma və yazma tarazlıdırsa
  • Critical section qısadırsa
  • Sadəlik vacibdirsə

Limitlər və Alternativlər

// ReadWriteLock - yaxşı, amma daha yaxşısı var
ReadWriteLock rwLock = new ReentrantReadWriteLock();

// StampedLock - Java 8+, daha performanslı
StampedLock stampedLock = new StampedLock();

// ConcurrentHashMap - Map üçün ən yaxşısı
ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>();

// CopyOnWriteArrayList - List üçün read-heavy
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();

Özət

ReadWriteLock üstünlükləri:

  • Paralel oxuma imkanı
  • Read-heavy workload-da performans artımı
  • Write əməliyyatları eksklüzivdir

Çatışmazlıqlar:

  • synchronized-dən mürəkkəbdir
  • Write-heavy ssenarilərdə üstünlük vermir
  • Overhead var (lock management)