Thread Safety and Locking Mechanisms in Java

Master thread safety, synchronization, locks, concurrent collections, atomic classes, and thread-safe code patterns for the OCP 21 exam.

Table of Contents

1. Thread Safety

Thread safety ensures that shared data can be safely accessed by multiple threads without data corruption or inconsistent state.

1.1 Race Conditions

Example:
// NOT thread-safe
class Counter {
    private int count = 0;
    
    public void increment() {
        count++;  // Race condition - not atomic
    }
    
    public int getCount() {
        return count;
    }
}

// Thread-safe version
class SafeCounter {
    private int count = 0;
    
    public synchronized void increment() {
        count++;
    }
    
    public synchronized int getCount() {
        return count;
    }
}

2. Synchronization

2.1 Synchronized Methods

Example:
class Example {
    private int value = 0;
    
    // Synchronized instance method
    public synchronized void increment() {
        value++;
    }
    
    // Synchronized static method
    public static synchronized void staticMethod() {
        // Synchronizes on Class object
    }
}

2.2 Synchronized Blocks

Example:
class Example {
    private final Object lock = new Object();
    private int value = 0;
    
    public void increment() {
        synchronized (lock) {  // Synchronize on specific object
            value++;
        }
    }
    
    public void method() {
        synchronized (this) {  // Synchronize on instance
            // Critical section
        }
    }
}

3. Lock Interface

The Lock interface provides more flexible locking than synchronized.

3.1 ReentrantLock

Example:
import java.util.concurrent.locks.ReentrantLock;

class Example {
    private final ReentrantLock lock = new ReentrantLock();
    private int value = 0;
    
    public void increment() {
        lock.lock();  // Acquire lock
        try {
            value++;
        } finally {
            lock.unlock();  // Always release in finally
        }
    }
    
    // Try lock with timeout
    public boolean tryIncrement() {
        try {
            if (lock.tryLock(1, TimeUnit.SECONDS)) {
                try {
                    value++;
                    return true;
                } finally {
                    lock.unlock();
                }
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        return false;
    }
}

3.2 ReadWriteLock

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

class DataStore {
    private final ReadWriteLock lock = new ReentrantReadWriteLock();
    private String data = "Initial";
    
    public String read() {
        lock.readLock().lock();
        try {
            return data;  // Multiple readers can read simultaneously
        } finally {
            lock.readLock().unlock();
        }
    }
    
    public void write(String newData) {
        lock.writeLock().lock();
        try {
            data = newData;  // Only one writer at a time
        } finally {
            lock.writeLock().unlock();
        }
    }
}

4. Atomic Classes

Atomic classes provide thread-safe operations on single variables without synchronization.

4.1 AtomicInteger and AtomicLong

Example:
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;

// Thread-safe counter
class Counter {
    private final AtomicInteger count = new AtomicInteger(0);
    
    public void increment() {
        count.incrementAndGet();  // Atomic operation
    }
    
    public int get() {
        return count.get();
    }
    
    // Compare and set
    public boolean compareAndSet(int expected, int update) {
        return count.compareAndSet(expected, update);
    }
}

// AtomicLong for long values
AtomicLong atomicLong = new AtomicLong(0L);
atomicLong.addAndGet(10);

4.2 AtomicReference

Example:
import java.util.concurrent.atomic.AtomicReference;

AtomicReference<String> ref = new AtomicReference<>("Initial");

// Update atomically
ref.set("New Value");

// Compare and set
boolean updated = ref.compareAndSet("Initial", "Updated");

// Get and set
String oldValue = ref.getAndSet("New Value");

5. Volatile Keyword

The volatile keyword ensures visibility of changes across threads but does not provide atomicity.

5.1 Volatile Variables

Example:
class Example {
    private volatile boolean flag = false;
    
    public void setFlag() {
        flag = true;  // Write is immediately visible to all threads
    }
    
    public boolean getFlag() {
        return flag;  // Always reads latest value
    }
}

// Volatile ensures visibility but NOT atomicity
// For compound operations, use synchronized or atomic classes
private volatile int count = 0;

public void increment() {
    count++;  // NOT atomic - still needs synchronization!

6. Exam Key Points

Critical Concepts for OCP 21 Exam:

  • Thread Safety: Code safe for concurrent access
  • Race Condition: When multiple threads access shared data unsafely
  • synchronized: Ensures only one thread executes critical section
  • synchronized method: Locks on instance (or Class for static)
  • synchronized block: Locks on specified object
  • ReentrantLock: More flexible than synchronized
  • ReadWriteLock: Allows multiple readers or single writer
  • Atomic Classes: Thread-safe operations without synchronization
  • AtomicInteger: Thread-safe integer operations
  • compareAndSet: Atomic compare-and-swap operation
  • volatile: Ensures visibility, not atomicity
  • Lock Ordering: Always acquire locks in same order to avoid deadlock
  • Deadlock: When threads wait for each other indefinitely

Post a Comment

0 Comments