Skip to content
IRC-Coding IRC-Coding
Multithreading Java Threads Synchronization Wait Notify Concurrent Locks Semaphores Thread Safety

Java Multithreading: Threads, Synchronization & Locks

Master Java multithreading: threads, synchronization, wait/notify, concurrent locks, semaphores, thread safety, and deadlock prevention.

S

schutzgeist

2 min read
Java Multithreading: Threads, Synchronization & Locks

Multithreading in Java: Threads, Synchronisation, Wait/Notify & Concurrent Locks

This post is a comprehensive guide to Java Multithreading – including threads, synchronisation, wait/notify, concurrent locks and semaphores with practical examples.

In a Nutshell

Multithreading enables parallel execution of tasks. Synchronisation ensures consistent data access, while concurrent locks provide modern mechanisms for controlling threads.

Compact Technical Description

Multithreading is the ability of a program to execute multiple threads simultaneously. Each thread has its own stack but shares heap memory with other threads.

Basic Concepts:

Thread Lifecycle

  • NEW: Thread created but not yet started
  • RUNNABLE: Ready for execution (running or ready)
  • BLOCKED: Waiting for monitor lock
  • WAITING: Waiting indefinitely for a condition
  • TIMED_WAITING: Waiting with time limit
  • TERMINATED: Thread terminated

Synchronisation Mechanisms

  • synchronized: Monitor-based synchronisation
  • wait()/notify(): Inter-thread communication
  • ReentrantLock: Flexible lock mechanism
  • ReadWriteLock: Separate read/write locks
  • Semaphore: Counter-based access control
  • CountDownLatch: Waiting for multiple threads
  • CyclicBarrier: Synchronisation point for threads

Thread Safety

  • Immutable Objects: Naturally thread-safe
  • Thread-Local: Thread-specific data
  • Volatile: Visibility of variables
  • Atomic Classes: Lock-free operations

Exam-Relevant Key Points

  • Threads: Lightweight processes with own stack
  • Synchronisation: Protection of shared resources
  • Monitor: Object-based synchronisation mechanism
  • wait/notify: Inter-thread communication
  • Deadlock: Deadlock through mutual blocking
  • Race Condition: Uncontrolled access to shared data
  • Volatile: Guarantees visibility between threads
  • IHK-relevant: Important for performant, parallel applications

Core Components

  1. Thread Management: Creation, control, lifecycle
  2. Synchronisation: synchronized, locks, semaphores
  3. Inter-Thread Communication: wait/notify, blocking queues
  4. Concurrent Collections: Thread-safe data structures
  5. Thread Pools: ExecutorService, ThreadPoolExecutor
  6. Thread Safety: Immutable, volatile, atomic classes
  7. Performance: Lock granularity, contention reduction
  8. Debugging: Thread dumps, race conditions, deadlocks

Practical Examples

1. Basic Thread Operations

public class ThreadGrundlagen {
    
    // Thread durch Vererbung von Thread
    static class MeinThread extends Thread {
        private String name;
        
        public MeinThread(String name) {
            this.name = name;
        }
        
        @Override
        public void run() {
            for (int i = 1; i <= 5; i++) {
                System.out.println(name + " - Zählung: " + i);
                try {
                    Thread.sleep(500); // 500ms Pause
                } catch (InterruptedException e) {
                    System.out.println(name + " wurde unterbrochen");
                    return;
                }
            }
            System.out.println(name + " beendet");
        }
    }
    
    // Thread durch Implementierung von Runnable
    static class MeinRunnable implements Runnable {
        private String name;
        
        public MeinRunnable(String name) {
            this.name = name;
        }
        
        @Override
        public void run() {
            for (int i = 1; i <= 3; i++) {
                System.out.println(name + " - Arbeit: " + i);
                try {
                    Thread.sleep(300);
                } catch (InterruptedException e) {
                    System.out.println(name + " unterbrochen");
                    return;
                }
            }
            System.out.println(name + " fertig");
        }
    }
    
    // Lambda-Ausdruck als Runnable
    static void lambdaThreadDemo() {
        Thread lambdaThread = new Thread(() -> {
            for (int i = 1; i <= 3; i++) {
                System.out.println("Lambda Thread - Schritt " + i);
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    return;
                }
            }
        });
        
        lambdaThread.start();
    }
    
    public static void main(String[] args) {
        System.out.println("=== Thread-Grundlagen ===");
        
        // Thread durch Vererbung
        MeinThread thread1 = new MeinThread("Thread-1");
        thread1.start();
        
        // Thread durch Runnable
        Thread thread2 = new Thread(new MeinRunnable("Runnable-1"));
        thread2.start();
        
        // Lambda Thread
        lambdaThreadDemo();
        
        // Auf Threads warten
        try {
            thread1.join();
            thread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
        System.out.println("Hauptthread beendet");
    }
}

2. Synchronisation with synchronized

public class SynchronisationDemo {
    
    // Gemeinsame Ressource
    static class Zaehler {
        private int wert = 0;
        
        // Synchronisierte Methode
        public synchronized void erhoehen() {
            wert++;
            System.out.println(Thread.currentThread().getName() + 
                             " erhöht auf: " + wert);
        }
        
        // Synchronisierter Block
        public void verringern() {
            synchronized(this) {
                wert--;
                System.out.println(Thread.currentThread().getName() + 
                                 " verringert auf: " + wert);
            }
        }
        
        public synchronized int getWert() {
            return wert;
        }
    }
    
    // Producer-Consumer mit wait/notify
    static class Warenlager {
        private final int[] lager = new int[5];
        private int index = 0;
        
        public synchronized void einlagern(int ware) throws InterruptedException {
            // Warten wenn Lager voll
            while (index >= lager.length) {
                System.out.println("Lager voll - Producer wartet");
                wait();
            }
            
            lager[index] = ware;
            index++;
            System.out.println(Thread.currentThread().getName() + 
                             " eingelagert: " + ware);
            
            // Consumer benachrichtigen
            notifyAll();
        }
        
        public synchronized int auslagern() throws InterruptedException {
            // Warten wenn Lager leer
            while (index <= 0) {
                System.out.println("Lager leer - Consumer wartet");
                wait();
            }
            
            index--;
            int ware = lager[index];
            System.out.println(Thread.currentThread().getName() + 
                             " ausgelagert: " + ware);
            
            // Producer benachrichtigen
            notifyAll();
            
            return ware;
        }
    }
    
    static class Producer implements Runnable {
        private Warenlager lager;
        
        public Producer(Warenlager lager) {
            this.lager = lager;
        }
        
        @Override
        public void run() {
            try {
                for (int i = 1; i <= 10; i++) {
                    lager.einlagern(i);
                    Thread.sleep(100);
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
    }
    
    static class Consumer implements Runnable {
        private Warenlager lager;
        
        public Consumer(Warenlager lager) {
            this.lager = lager;
        }
        
        @Override
        public void run() {
            try {
                for (int i = 1; i <= 10; i++) {
                    lager.auslagern();
                    Thread.sleep(150);
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
    }
    
    public static void main(String[] args) {
        System.out.println("=== Synchronisation Demo ===");
        
        // Einfache Synchronisation
        Zaehler zaehler = new Zaehler();
        
        Thread[] threads = new Thread[5];
        for (int i = 0; i < threads.length; i++) {
            threads[i] = new Thread(() -> {
                for (int j = 0; j < 3; j++) {
                    zaehler.erhoehen();
                    try {
                        Thread.sleep(50);
                    } catch (InterruptedException e) {
                        return;
                    }
                }
            });
            threads[i].setName("Thread-" + i);
        }
        
        // Threads starten
        for (Thread t : threads) {
            t.start();
        }
        
        // Auf Threads warten
        for (Thread t : threads) {
            try {
                t.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        
        System.out.println("Endwert: " + zaehler.getWert());
        
        // Producer-Consumer Demo
        System.out.println("\n=== Producer-Consumer Demo ===");
        Warenlager lager = new Warenlager();
        
        Thread producer = new Thread(new Producer(lager), "Producer");
        Thread consumer = new Thread(new Consumer(lager), "Consumer");
        
        producer.start();
        consumer.start();
        
        try {
            producer.join();
            consumer.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

3. Concurrent Locks and Modern Synchronization

import java.util.concurrent.locks.*;
import java.util.concurrent.*;

public class ConcurrentLocksDemo {
    
    // ReentrantLock Example
    static class Bankkonto {
        private double kontostand;
        private final ReentrantLock lock = new ReentrantLock();
        
        public Bankkonto(double startbetrag) {
            this.kontostand = startbetrag;
        }
        
        public void einzahlen(double betrag) {
            lock.lock();
            try {
                double alterStand = kontostand;
                Thread.sleep(50); // Simulate processing
                kontostand = alterStand + betrag;
                System.out.println(Thread.currentThread().getName() + 
                                 " deposited: " + betrag + 
                                 ", new balance: " + kontostand);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            } finally {
                lock.unlock();
            }
        }
        
        public boolean abheben(double betrag) {
            lock.lock();
            try {
                if (kontostand >= betrag) {
                    double alterStand = kontostand;
                    Thread.sleep(50);
                    kontostand = alterStand - betrag;
                    System.out.println(Thread.currentThread().getName() + 
                                     " withdrew: " + betrag + 
                                     ", new balance: " + kontostand);
                    return true;
                } else {
                    System.out.println(Thread.currentThread().getName() + 
                                     " Could not withdraw: " + betrag + 
                                     " (balance: " + kontostand + ")");
                    return false;
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                return false;
            } finally {
                lock.unlock();
            }
        }
        
        public double getKontostand() {
            lock.lock();
            try {
                return kontostand;
            } finally {
                lock.unlock();
            }
        }
    }
    
    // ReadWriteLock Example
    static class ThreadSafeList {
        private final List<String> liste = new ArrayList<>();
        private final ReadWriteLock rwLock = new ReentrantReadWriteLock();
        private final Lock readLock = rwLock.readLock();
        private final Lock writeLock = rwLock.writeLock();
        
        public void add(String element) {
            writeLock.lock();
            try {
                liste.add(element);
                System.out.println(Thread.currentThread().getName() + 
                                 " added: " + element);
            } finally {
                writeLock.unlock();
            }
        }
        
        public String get(int index) {
            readLock.lock();
            try {
                return liste.get(index);
            } finally {
                readLock.unlock();
            }
        }
        
        public List<String> getAll() {
            readLock.lock();
            try {
                return new ArrayList<>(liste); // Return copy
            } finally {
                readLock.unlock();
            }
        }
        
        public int size() {
            readLock.lock();
            try {
                return liste.size();
            } finally {
                readLock.unlock();
            }
        }
    }
    
    // Semaphore Example
    static class RessourcenPool {
        private final Semaphore semaphore;
        private final List<String> ressourcen;
        
        public RessourcenPool(int maxRessourcen) {
            semaphore = new Semaphore(maxRessourcen);
            ressourcen = new ArrayList<>();
            for (int i = 1; i <= maxRessourcen; i++) {
                ressourcen.add("Ressource-" + i);
            }
        }
        
        public String acquire() throws InterruptedException {
            semaphore.acquire();
            
            synchronized(ressourcen) {
                if (!ressourcen.isEmpty()) {
                    String ressource = ressourcen.remove(0);
                    System.out.println(Thread.currentThread().getName() + 
                                     " acquired: " + ressource);
                    return ressource;
                }
            }
            
            semaphore.release();
            return null;
        }
        
        public void release(String ressource) {
            synchronized(ressourcen) {
                ressourcen.add(ressource);
                System.out.println(Thread.currentThread().getName() + 
                                 " released: " + ressource);
            }
            semaphore.release();
        }
    }
    
    // CountDownLatch Example
    static class Worker implements Runnable {
        private final CountDownLatch startSignal;
        private final CountDownLatch doneSignal;
        private final int workerId;
        
        public Worker(CountDownLatch startSignal, CountDownLatch doneSignal, int workerId) {
            this.startSignal = startSignal;
            this.doneSignal = doneSignal;
            this.workerId = workerId;
        }
        
        @Override
        public void run() {
            try {
                // Wait for start signal
                System.out.println("Worker " + workerId + " ready");
                startSignal.await();
                
                // Perform work
                System.out.println("Worker " + workerId + " working");
                Thread.sleep((long) (Math.random() * 1000));
                
                System.out.println("Worker " + workerId + " done");
                
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            } finally {
                doneSignal.countDown();
            }
        }
    }
    
    public static void main(String[] args) {
        System.out.println("=== Concurrent Locks Demo ===");
        
        // ReentrantLock Demo
        Bankkonto konto = new Bankkonto(1000.0);
        
        Thread[] bankThreads = new Thread[4];
        for (int i = 0; i < bankThreads.length; i++) {
            final int threadId = i;
            bankThreads[i] = new Thread(() -> {
                for (int j = 0; j < 3; j++) {
                    if (threadId % 2 == 0) {
                        konto.einzahlen(100);
                    } else {
                        konto.abheben(50);
                    }
                }
            }, "BankThread-" + i);
        }
        
        for (Thread t : bankThreads) {
            t.start();
        }
        
        for (Thread t : bankThreads) {
            try {
                t.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        
        System.out.println("Final balance: " + konto.getKontostand());
        
        // ReadWriteLock Demo
        System.out.println("\n=== ReadWriteLock Demo ===");
        ThreadSafeList liste = new ThreadSafeList();
        
        // Writer Thread
        Thread writer = new Thread(() -> {
            for (int i = 1; i <= 5; i++) {
                liste.add("Element-" + i);
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    return;
                }
            }
        }, "Writer");
        
        // Reader Threads
        Thread[] readers = new Thread[3];
        for (int i = 0; i < readers.length; i++) {
            readers[i] = new Thread(() -> {
                for (int j = 0; j < 10; j++) {
                    List<String> alle = liste.getAll();
                    System.out.println(Thread.currentThread().getName() + 
                                     " read: " + alle.size() + " elements");
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        return;
                    }
                }
            }, "Reader-" + i);
        }
        
        writer.start();
        for (Thread reader : readers) {
            reader.start();
        }
        
        try {
            writer.join();
            for (Thread reader : readers) {
                reader.join();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
        // Semaphore Demo
        System.out.println("\n=== Semaphore Demo ===");
        RessourcenPool pool = new RessourcenPool(2);
        
        Thread[] poolThreads = new Thread[5];
        for (int i = 0; i < poolThreads.length; i++) {
            poolThreads[i] = new Thread(() -> {
                try {
                    String ressource = pool.acquire();
                    if (ressource != null) {
                        Thread.sleep(1000); // Use resource
                        pool.release(ressource);
                    }
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }, "PoolThread-" + i);
        }
        
        for (Thread t : poolThreads) {
            t.start();
        }
        
        for (Thread t : poolThreads) {
            try {
                t.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        
        // CountDownLatch Demo
        System.out.println("\n=== CountDownLatch Demo ===");
        int workerCount = 3;
        CountDownLatch startSignal = new CountDownLatch(1);
        CountDownLatch doneSignal = new CountDownLatch(workerCount);
        
        for (int i = 1; i <= workerCount; i++) {
            new Thread(new Worker(startSignal, doneSignal, i)).start();
        }
        
        try {
            Thread.sleep(1000);
            System.out.println("All workers ready - Start signal!");
            startSignal.countDown();
            
            doneSignal.await();
            System.out.println("All workers done!");
            
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

4. Thread-Safe Data Structures and Atomic Classes

import java.util.concurrent.atomic.*;
import java.util.concurrent.*;

public class ThreadSafeCollections {
    
    // Atomic Classes Demo
    static class AtomicZaehler {
        private final AtomicInteger zaehler = new AtomicInteger(0);
        private final AtomicLong longZaehler = new AtomicLong(0);
        private final AtomicBoolean flag = new AtomicBoolean(false);
        private final AtomicReference<String> nachricht = new AtomicReference<>("");
        
        public void increment() {
            int alterWert = zaehler.getAndIncrement();
            System.out.println(Thread.currentThread().getName() + 
                             " increment: " + alterWert + " -> " + zaehler.get());
        }
        
        public void add(long wert) {
            long alterWert = longZaehler.getAndAdd(wert);
            System.out.println(Thread.currentThread().getName() + 
                             " add: " + alterWert + " + " + wert + " -> " + longZaehler.get());
        }
        
        public void toggleFlag() {
            boolean alterWert = flag.getAndSet(!flag.get());
            System.out.println(Thread.currentThread().getName() + 
                             " toggle: " + alterWert + " -> " + flag.get());
        }
        
        public void updateNachricht(String neueNachricht) {
            String alteNachricht = nachricht.getAndSet(neueNachricht);
            System.out.println(Thread.currentThread().getName() + 
                             " update: '" + alteNachricht + "' -> '" + neueNachricht + "'");
        }
        
        public int getZaehler() { return zaehler.get(); }
        public long getLongZaehler() { return longZaehler.get(); }
        public boolean getFlag() { return flag.get(); }
        public String getNachricht() { return nachricht.get(); }
    }
    
    // Concurrent Collections Demo
    static class ConcurrentCollectionsDemo {
        
        public static void hashMapDemo() {
            System.out.println("=== ConcurrentHashMap Demo ===");
            ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
            
            // Writer Threads
            Thread[] writers = new Thread[3];
            for (int i = 0; i < writers.length; i++) {
                final int threadId = i;
                writers[i] = new Thread(() -> {
                    for (int j = 0; j < 5; j++) {
                        String key = "Key-" + threadId + "-" + j;
                        map.put(key, threadId * 100 + j);
                        System.out.println(Thread.currentThread().getName() + 
                                         " put: " + key);
                        try {
                            Thread.sleep(50);
                        } catch (InterruptedException e) {
                            return;
                        }
                    }
                }, "Writer-" + i);
            }
            
            // Reader Thread
            Thread reader = new Thread(() -> {
                for (int i = 0; i < 20; i++) {
                    System.out.println(Thread.currentThread().getName() + 
                                     " size: " + map.size());
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        return;
                    }
                }
            }, "Reader");
            
            // Start all threads
            reader.start();
            for (Thread writer : writers) {
                writer.start();
            }
            
            // Wait for threads
            try {
                for (Thread writer : writers) {
                    writer.join();
                }
                reader.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            
            System.out.println("Final map size: " + map.size());
        }
        
        public static void blockingQueueDemo() {
            System.out.println("\n=== BlockingQueue Demo ===");
            BlockingQueue<String> queue = new ArrayBlockingQueue<>(5);
            
            // Producer
            Thread producer = new Thread(() -> {
                try {
                    for (int i = 1; i <= 10; i++) {
                        String item = "Item-" + i;
                        queue.put(item);
                        System.out.println("Producer put: " + item + 
                                         " (queue size: " + queue.size() + ")");
                        Thread.sleep(200);
                    }
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }, "Producer");
            
            // Consumer
            Thread consumer = new Thread(() -> {
                try {
                    for (int i = 1; i <= 10; i++) {
                        String item = queue.take();
                        System.out.println("Consumer take: " + item + 
                                         " (queue size: " + queue.size() + ")");
                        Thread.sleep(300);
                    }
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }, "Consumer");
            
            producer.start();
            consumer.start();
            
            try {
                producer.join();
                consumer.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
    // ThreadLocal Demo
    static class ThreadLocalDemo {
        private static ThreadLocal<Integer> threadLocalValue = ThreadLocal.withInitial(() -> 100);
        private static ThreadLocal<String> threadLocalName = new ThreadLocal<>();
        
        public static void demo() {
            System.out.println("=== ThreadLocal Demo ===");
            
            Thread[] threads = new Thread[3];
            for (int i = 0; i < threads.length; i++) {
                final int threadId = i;
                threads[i] = new Thread(() -> {
                    threadLocalName.set("Thread-" + threadId);
                    
                    for (int j = 0; j < 3; j++) {
                        int wert = threadLocalValue.get();
                        String name = threadLocalName.get();
                        
                        System.out.println(name + " wert: " + wert);
                        
                        // Change ThreadLocal value
                        threadLocalValue.set(wert + threadId);
                        
                        try {
                            Thread.sleep(200);
                        } catch (InterruptedException e) {
                            return;
                        }
                    }
                    
                    // Clean up ThreadLocal
                    threadLocalName.remove();
                    threadLocalValue.remove();
                }, "Thread-" + i);
            }
            
            for (Thread t : threads) {
                t.start();
            }
            
            for (Thread t : threads) {
                try {
                    t.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    public static void main(String[] args) {
        // Atomic Classes Demo
        System.out.println("=== Atomic Classes Demo ===");
        AtomicZaehler zaehler = new AtomicZaehler();
        
        Thread[] atomicThreads = new Thread[4];
        for (int i = 0; i < atomicThreads.length; i++) {
            final int threadId = i;
            atomicThreads[i] = new Thread(() -> {
                zaehler.increment();
                zaehler.add(threadId * 10);
                zaehler.toggleFlag();
                zaehler.updateNachricht("Nachricht von Thread-" + threadId);
            }, "AtomicThread-" + i);
        }
        
        for (Thread t : atomicThreads) {
            t.start();
        }
        
        for (Thread t : atomicThreads) {
            try {
                t.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        
        System.out.println("Final values:");
        System.out.println("Zaehler: " + zaehler.getZaehler());
        System.out.println("LongZaehler: " + zaehler.getLongZaehler());
        System.out.println("Flag: " + zaehler.getFlag());
        System.out.println("Nachricht: " + zaehler.getNachricht());
        
        // Concurrent Collections Demo
        ConcurrentCollectionsDemo.hashMapDemo();
        ConcurrentCollectionsDemo.blockingQueueDemo();
        
        // ThreadLocal Demo
        ThreadLocalDemo.demo();
    }
}

Thread Pools and ExecutorService

ThreadPoolExecutor

// Fixed Thread Pool
ExecutorService fixedPool = Executors.newFixedThreadPool(4);

// Cached Thread Pool
ExecutorService cachedPool = Executors.newCachedThreadPool();

// Single Thread Executor
ExecutorService singlePool = Executors.newSingleThreadExecutor();

// Scheduled Thread Pool
ScheduledExecutorService scheduledPool = Executors.newScheduledThreadPool(2);

// Execute tasks
Future<String> future = fixedPool.submit(() -> {
    Thread.sleep(1000);
    return "Result";
});

// Scheduled tasks
scheduledPool.scheduleAtFixedRate(() -> {
    System.out.println("Periodic task");
}, 0, 1, TimeUnit.SECONDS);

// Shutdown pool
fixedPool.shutdown();
scheduledPool.shutdown();

Deadlock Prevention

Deadlock Conditions

  1. Mutual Exclusion: Resource can only be used by one thread
  2. Hold and Wait: Thread holds resources and waits for more
  3. No Preemption: Resources cannot be forcibly released
  4. Circular Wait: Circular waiting loop between threads

Avoidance Strategies

// Lock Ordering - always lock in the same order
public void transfer(Account from, Account to, double amount) {
    // Synchronize accounts in consistent order
    Account first = from.getId() < to.getId() ? from : to;
    Account second = from.getId() < to.getId() ? to : from;
    
    synchronized(first) {
        synchronized(second) {
            from.withdraw(amount);
            to.deposit(amount);
        }
    }
}

// TryLock with timeout
public boolean transferWithTryLock(Account from, Account to, double amount) {
    while (true) {
        try {
            if (from.getLock().tryLock(1, TimeUnit.SECONDS)) {
                try {
                    if (to.getLock().tryLock(1, TimeUnit.SECONDS)) {
                        try {
                            from.withdraw(amount);
                            to.deposit(amount);
                            return true;
                        } finally {
                            to.getLock().unlock();
                        }
                    }
                } finally {
                    from.getLock().unlock();
                }
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            return false;
        }
        // Short pause before retry
        Thread.sleep(100);
    }
}

Performance Optimization

Lock Granularity

// Coarse-grained synchronization (bad)
public synchronized void addElement(Object element) {
    // Entire operation locked
    list.add(element);
    size++;
}

// Fine-grained synchronization (better)
public void addElement(Object element) {
    synchronized(list) {
        list.add(element);
    }
    synchronized(this) {
        size++;
    }
}

Volatile vs Synchronized

// Volatile for simple visibility
private volatile boolean running = true;

public void stop() {
    running = false; // Visible to all threads
}

public void run() {
    while (running) {
        // Perform work
    }
}

// Synchronized for complex operations
private int counter = 0;

public synchronized void increment() {
    counter++; // Atomic operation
}

Advantages and Disadvantages

Advantages of Multithreading

  • Performance: Parallel execution on multi-core systems
  • Responsiveness: UI remains responsive during long operations
  • Resource utilization: Better utilization of system resources
  • Scalability: Tasks can be distributed

Disadvantages

  • Complexity: Synchronization is error-prone
  • Debugging: Race conditions are hard to reproduce
  • Overhead: Thread creation and context switching cost time
  • Resources: More memory and CPU consumption

Common Exam Questions

  1. What is the difference between wait() and sleep()? wait() releases the lock, sleep() retains the lock. wait() requires synchronized, sleep() does not.

  2. Explain deadlock and how to avoid it! Deadlock is mutual blocking. Avoidance through lock ordering, tryLock with timeout.

  3. When do you use volatile instead of synchronized? volatile for simple visibility of variables, synchronized for complex operations.

  4. What is the difference between Runnable and Callable? Runnable has no return value, Callable returns a result and can throw exceptions.

Most Important Sources

  1. https://docs.oracle.com/javase/tutorial/essential/concurrency/
  2. https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/package-summary.html
  3. https://www.baeldung.com/java-concurrency
Back to Blog
Share: