Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

  • multi-threading is an ability of a program to execute multiple threads concurrently, where each thread represents an independent flow of execution.
  • threads share the same memory space within a process, making them lightweight compared to separate processes.
  • benefits -
    • concurrency - perform multiple tasks simultaneosly
    • responsiveness - keep application responsive
    • efficiency - utilizes cpu more effectively
    • modularity - break complex tasks into independent threads.
  • challenges -
    • race conditions.
    • deadlocks - thread waiting for each other infinitely.
    • thread safety - ensuring shared resources are accessed correctly.
  • thread lifecycle -
    • new - thread created but not started
    • runnable - thread is ready to run or running
    • blocked/waiting - thread is waiting for a monitor lock or another condition.
    • timed waiting - waiting for a specific amount of time.
    • terminated - thread has completed execution.

main thread

  • it is the default thread created when a java program starts.
  • created automatically by jvm.
  • responsible for executing main() method
  • can create and manage other threads.
  • program terminates when the main thread ends.
public class MainThread {
    public static void main(String[] args) {
        Thread current = Thread.currentThread(); // Get main thread
        System.out.println("Main thread: " + current.getName());
        System.out.println("Priority: " + current.getPriority());
    }
}

java thread model

it provides a robust thread model through java.lang.Thread class and java.lang.Runnnable interface. threads can be created in two ways -

  • extending the thread class
class MyThread extends Thread {
    @Override
    public void run() {
        for (int i = 1; i <= 3; i++) {
            System.out.println(getName() + ": Count " + i);
        }
    }
}

public class Main {
    public static void main(String[] args) {
        MyThread t1 = new MyThread();
        t1.start(); // Start thread
    }
}
  • implementing runnable interface
class MyRunnable implements Runnable {
    @Override
    public void run() {
        for (int i = 1; i <= 3; i++) {
            System.out.println(Thread.currentThread().getName() + ": Count " + i);
        }
    }
}

public class Main {
    public static void main(String[] args) {
        Thread t1 = new Thread(new MyRunnable(), "RunnableThread");
        t1.start();
    }
}

key methods

  • start(): Begins thread execution (calls run()).
  • run(): Contains the thread’s task (override in subclass or Runnable).
  • sleep(long millis): Pauses the thread for the specified time.
  • join(): Waits for the thread to terminate.
  • setName(String name): Sets the thread’s name.
  • getName(): Gets the thread’s name.
  • setPriority(int priority): Sets the thread’s priority.
  • isAlive(): Checks if the thread is running.

thread priorities

thread priorities determine the relative importance of threads, influencing the schedular’s decision on which thread to execute when multiple threads are runnable. Java assigns prirorites as integer from 1 to 10.

Thread t1 = new Thread(() -> System.out.println("Low priority"));
t1.setPriority(Thread.MIN_PRIORITY);

Thread t2 = new Thread(() -> System.out.println("High priority"));
t2.setPriority(Thread.MAX_PRIORITY);

t1.start();
t2.start();

synchronization

  • when multiple threads access the same shared resources, race conditions can occur, leading to inconsistent or incorrect results.
  • Synchronization ensures that only one thread can access the data once at a time.
  • mechanism -
    • synchronization methods - add synchronized keyword to a method to ensure only one thread can execute at a time for a given object. uses the object’s intrinsic lock (monitor).
    • synchronized blocks - synchronize a specific block of code using an object’s lock
// synchronization method
class Counter {
    private int count = 0;

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

    public int getCount() {
        return count;
    }
}

// synchronization blocks
class Counter {
    private int count = 0;
    private final Object lock = new Object();

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

    public int getCount() {
        return count;
    }
}

interthread communication

  • interthread communication allows threads to coordinate their actions, typically using the wait-notify mechanism to avoid busy-waiting (polling).
  • this is useful when one thread needs to wait for another to complete a task or update a shared resource.

key methods

  • wait() - causes the current thread to wait until another thread calls notify().
  • notify() - wakes up one waiting thread
  • notifyall() - wakes up all waiting threads.
class SharedBuffer {
    private int data;
    private boolean available = false;

    public synchronized void produce(int value) throws InterruptedException {
        while (available) {
            wait(); // Wait if buffer is full
        }
        data = value;
        available = true;
        System.out.println("Produced: " + data);
        notifyAll();
    }

    public synchronized int consume() throws InterruptedException {
        while (!available) {
            wait(); // Wait if buffer is empty
        }
        available = false;
        System.out.println("Consumed: " + data);
        notifyAll();
        return data;
    }
}

public class Main {
    public static void main(String[] args) {
        SharedBuffer buffer = new SharedBuffer();

        Thread producer = new Thread(() -> {
            try {
                for (int i = 1; i <= 5; i++) {
                    buffer.produce(i);
                    Thread.sleep(1000);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        Thread consumer = new Thread(() -> {
            try {
                for (int i = 1; i <= 5; i++) {
                    buffer.consume();
                    Thread.sleep(2000);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        producer.start();
        consumer.start();
    }
}