Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Java Concurrency Fundamentals: Core Concepts and Mechanisms

Tech May 15 1

Concurrency Theory

Why Multithreading is Necessary

There are significant speed differences between CPU, memory, and I/O devices. To effectively utilize CPU performance and balance these speed disparities, computer architecture, operating systems, and compilers contribute through:

  • CPU caching to balance memory speed differences (causes visibility issues)
  • Operating system processes and threads for CPU time-sharing to balance CPU and I/O speed differences (causes atomicity issues)
  • Compiler optimization of instruction execution order for better cache utilization (causes ordering issues)

Root Causes of Concurrency Problems: Three Core Issues

Visibility: CPU Cache Issues

Visibility means modifications to shared variables by one thread are immediately visible to other threads.

// Thread A execution
int counter = 0;
counter = 10;

// Thread B execution
int result = counter;

If Thread A runs on CPU1 and Thread B on CPU2, when Thread A executes counter=10, it loads the initial value into CPU1's cache, assigns 10, but may not immediately write to main memory. Thread B executing result=counter reads from main memory where counter might still be 0, resulting in result being 0 instead of 10.

Atomicity: Time-sharing Issues

Atomicity means operations either execute completely without interrpution or don't execute at all.

int value = 1;

// Thread A
value += 1;

// Thread B  
value += 1;

The operation value += 1 requires three CPU instructions:

  1. Read variable from memory to CPU register
  2. Execute value + 1 in register
  3. Write result to memory (may go to CPU cache due to caching)

Due to thread switching, Thread A might execute the first instruction then switch to Thread B, which executes all three instructions. When switching back to Thread A, the final value written to memory could be 2 instead of 3.

Ordering: Instruction Reordering Issues

Ordering means program execution follows code sequence order.

int index = 0;        
boolean ready = false;
index = 1;           // statement 1  
ready = true;        // statement 2

Compilers and processors may reorder instructions to improve performance. The three types of reordering are:

  • Compiler optimization reordering
  • Instruction-level parallelism reordering
  • Memory system reordering

Java Memory Model (JMM) uses memory barriers to restrict certain types of reordering.

Thread Fundamentals

Process and Thread Concepts

Process

A process is a program in execution with independent memory space. Processes are resource allocation units with significant context switching overhead but offer stability and security.

Thread

A thread is a process entity and basic CPU scheduling unit. Threads share process resources with minimal own resources (program counter, registers, stack). Thread communication uses shared memory with fast context switching but less stability.

Coroutine

Coroutines are user-mode lightweight threads with user-controlled scheduling. They maintain register context and stack with minimal kernel switching overhead.

Thread State Transitions

  • NEW: Created but not started
  • RUNNABLE: May be running or waiting for CPU time slice
  • BLOCKED: Waiting for exclusive lock
  • WAITING: Waiting for explicit wake-up by other threads
  • TIMED_WAITING: Waiting with time limit
  • TERMINATED: Completed execution or terminated due to exception

Thread Implementation Methods

Runnable Interface

public class TaskRunner implements Runnable {
    private String message;
    
    public TaskRunner(String msg) {
        this.message = msg;
    }
    
    @Override
    public void run() {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Processing: " + this.message);
    }
    
    public static void main(String[] args) {
        Runnable task = new TaskRunner("test task");
        new Thread(task).start();
    }
}

Callable Interface

public class ComputeTask implements Callable<integer> {
    @Override
    public Integer call() throws Exception {
        return 456;
    }
}

public static void main(String[] args) throws Exception {
    ComputeTask task = new ComputeTask();
    FutureTask<integer> future = new FutureTask<>(task);
    Thread worker = new Thread(future);
    worker.start();
    System.out.println(future.get());
}
</integer></integer>

Thread Class Inheritance

class WorkerThread extends Thread {
    @Override
    public void run() {
        // task implementation
    }
}

WorkerThread worker = new WorkerThread();
worker.start();

Basic Thread Mechanisms

Executor Framework

public static void main(String[] args) {
    ExecutorService executor = Executors.newCachedThreadPool();
    for (int i = 0; i < 5; i++) {
        executor.execute(new TaskRunner("task-" + i));
    }
    executor.shutdown();
}

Daemon Threads

public static void main(String[] args) {
    Thread daemon = new Thread(new TaskRunner("daemon"));
    daemon.setDaemon(true);
    daemon.start();
}

Thread Sleep

public void execute() {
    try {
        Thread.sleep(3000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

Thread Yield

public class YieldExample {
    public static void main(String[] args) {
        new Thread(() -> {
            int count = 0;
            while (true) {
                count++;
                System.out.println(Thread.currentThread().getName() + " count: " + count);
            }
        }).start();
        
        new Thread(() -> {
            int count = 0;
            while (true) {
                Thread.yield();
                count++;
                System.out.println(Thread.currentThread().getName() + " count: " + count);
            }
        }).start();
    }
}

Thread Interruption

InterruptedException Handling

public class InterruptHandler {
    private static class SleepThread extends Thread {
        @Override
        public void run() {
            try {
                Thread.sleep(2000);
                System.out.println("Thread completed");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
    public static void main(String[] args) {
        Thread sleeper = new SleepThread();
        sleeper.start();
        sleeper.interrupt();
    }
}

Interruption Status Checking

public class InterruptCheck {
    public static void main(String[] args) {
        Thread worker = new Thread(() -> {
            while (!Thread.currentThread().isInterrupted()) {
                // perform work
            }
            System.out.println("Thread terminated via interruption");
        });
        worker.start();
        worker.interrupt();
    }
}

Thread Coordination

Wait and Notify

public class CoordinationExample {
    public synchronized void signal() {
        System.out.println("signaling");
        notifyAll();
    }

    public synchronized void waitForSignal() {
        try {
            wait();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("received signal");
    }
}

Thread Join

public class JoinDemo {
    private class First extends Thread {
        @Override
        public void run() {
            System.out.println("First thread");
        }
    }

    private class Second extends Thread {
        private First first;
        Second(First f) { this.first = f; }
        
        @Override
        public void run() {
            try {
                first.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("Second thread");
        }
    }
    
    public void demo() {
        First f = new First();
        Second s = new Second(f);
        s.start();
        f.start();
    }
}

Thread Synchronization

Synchronized Blocks

public class SyncExample {
    public void syncMethod() {
        synchronized (this) {
            for (int i = 0; i < 10; i++) {
                System.out.print(i + " ");
            }
        }
    }
}

ReentrantLock

public class LockDemo {
    private Lock lock = new ReentrantLock();

    public void lockedMethod() {
        lock.lock();
        try {
            for (int i = 0; i < 10; i++) {
                System.out.print(i + " ");
            }
        } finally {
            lock.unlock();
        }
    }
}

Advanced Thread Coordination

Condition Variables

public class ConditionExample {
    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();

    public void prepare() {
        lock.lock();
        try {
            System.out.println("preparation complete");
            condition.signalAll();
        } finally {
            lock.unlock();
        }
    }

    public void await() {
        lock.lock();
        try {
            condition.await();
            System.out.println("proceeding after signal");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.