Introduction to Multithreading

Introduction

Multithreading allows concurrent execution of multiple parts of a program. Each part is called a thread.


What is Thread?

Thread = lightweight subprocess, smallest unit of processing.

Benefits:

  • Better CPU utilization
  • Concurrent operations
  • Responsive applications
  • Better resource sharing

Process vs Thread

ProcessThread
Heavy weightLight weight
Separate memoryShared memory
IndependentPart of process
Expensive creationCheap creation
Communication slowCommunication fast

Creating Threads

Method 1: Extending Thread Class

class MyThread extends Thread {
    @Override
    public void run() {
        for (int i = 1; i <= 5; i++) {
            System.out.println(Thread.currentThread().getName() + ": " + i);
        }
    }
}

public class Main {
    public static void main(String[] args) {
        MyThread t1 = new MyThread();
        MyThread t2 = new MyThread();

        t1.start();  // Start thread
        t2.start();
    }
}

Method 2: Implementing Runnable Interface

class MyRunnable implements Runnable {
    @Override
    public void run() {
        for (int i = 1; i <= 5; i++) {
            System.out.println(Thread.currentThread().getName() + ": " + i);
        }
    }
}

public class Main {
    public static void main(String[] args) {
        MyRunnable r = new MyRunnable();
        Thread t1 = new Thread(r);
        Thread t2 = new Thread(r);

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

start() vs run()

// start() - creates new thread
t1.start();  // ✓ New thread created

// run() - executes in same thread
t1.run();    // ✗ No new thread, runs in main thread

Thread States

New → Runnable → Running → Terminated
           ↑          ↓
           ← Waiting/Blocked
  1. New: Thread created
  2. Runnable: ready to run
  3. Running: executing
  4. Waiting/Blocked: waiting for resource
  5. Terminated: finished execution

Thread Methods

getName() and setName():

Thread t = new Thread();
t.setName("MyThread");
System.out.println(t.getName());

currentThread():

Thread t = Thread.currentThread();
System.out.println("Current: " + t.getName());

sleep():

try {
    Thread.sleep(1000);  // Sleep 1 second
} catch (InterruptedException e) {
    e.printStackTrace();
}

join():

Thread t1 = new Thread();
t1.start();
t1.join();  // Wait for t1 to complete

Simple Example

class Counter extends Thread {
    String name;

    Counter(String name) {
        this.name = name;
    }

    @Override
    public void run() {
        for (int i = 1; i <= 5; i++) {
            System.out.println(name + ": " + i);
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

public class Main {
    public static void main(String[] args) {
        Counter c1 = new Counter("Thread-1");
        Counter c2 = new Counter("Thread-2");

        c1.start();
        c2.start();
    }
}

Thread Priority

Priority range: 1 (MIN) to 10 (MAX), Default: 5 (NORM)

public class Main {
    public static void main(String[] args) {
        Thread t1 = new Thread();
        Thread t2 = new Thread();

        t1.setPriority(Thread.MIN_PRIORITY);   // 1
        t2.setPriority(Thread.MAX_PRIORITY);   // 10

        System.out.println("t1 priority: " + t1.getPriority());
        System.out.println("t2 priority: " + t2.getPriority());

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

Lambda with Threads

public class Main {
    public static void main(String[] args) {
        // Lambda expression
        Thread t = new Thread(() -> {
            for (int i = 1; i <= 5; i++) {
                System.out.println("Count: " + i);
            }
        });

        t.start();
    }
}

Multiple Threads Example

class Task implements Runnable {
    String taskName;

    Task(String taskName) {
        this.taskName = taskName;
    }

    @Override
    public void run() {
        for (int i = 1; i <= 3; i++) {
            System.out.println(taskName + " - Step " + i);
            try {
                Thread.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println(taskName + " completed");
    }
}

public class Main {
    public static void main(String[] args) {
        Thread t1 = new Thread(new Task("Download"));
        Thread t2 = new Thread(new Task("Upload"));
        Thread t3 = new Thread(new Task("Process"));

        t1.start();
        t2.start();
        t3.start();
    }
}

Main Thread

public class Main {
    public static void main(String[] args) {
        Thread mainThread = Thread.currentThread();
        System.out.println("Main thread: " + mainThread.getName());
        System.out.println("Priority: " + mainThread.getPriority());

        mainThread.setName("MyMainThread");
        System.out.println("New name: " + mainThread.getName());
    }
}

Thread Synchronization (Brief)

Problem: Multiple threads accessing shared resource.

class Counter {
    int count = 0;

    // Synchronized method
    synchronized void increment() {
        count++;
    }
}

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

        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                counter.increment();
            }
        });

        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                counter.increment();
            }
        });

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

        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("Count: " + counter.count);
    }
}

Daemon Thread

Background thread that doesn’t prevent JVM from exiting.

public class Main {
    public static void main(String[] args) {
        Thread t = new Thread(() -> {
            while (true) {
                System.out.println("Daemon running");
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        t.setDaemon(true);  // Make daemon
        t.start();

        System.out.println("Main ends");
        // JVM exits, daemon thread stops
    }
}

Complete Example: File Downloader

class FileDownloader extends Thread {
    String fileName;
    int size;

    FileDownloader(String fileName, int size) {
        this.fileName = fileName;
        this.size = size;
    }

    @Override
    public void run() {
        System.out.println("Downloading " + fileName + "...");

        for (int i = 0; i <= size; i += 10) {
            System.out.println(fileName + ": " + i + "%");
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        System.out.println(fileName + " downloaded!");
    }
}

public class Main {
    public static void main(String[] args) {
        FileDownloader file1 = new FileDownloader("Image.jpg", 100);
        FileDownloader file2 = new FileDownloader("Video.mp4", 100);
        FileDownloader file3 = new FileDownloader("Document.pdf", 100);

        file1.start();
        file2.start();
        file3.start();

        System.out.println("All downloads started");
    }
}

Thread vs Runnable

Prefer Runnable:

  • Can extend other classes
  • More flexible
  • Better design

Use Thread:

  • Simple cases
  • Need to override Thread methods

Common Methods Summary

MethodDescription
start()Start thread
run()Thread code
sleep(ms)Pause thread
join()Wait for completion
getName()Get thread name
setName()Set thread name
getPriority()Get priority
setPriority()Set priority
currentThread()Get current thread
isAlive()Check if running

Quick Reference

// Method 1: Extend Thread
class MyThread extends Thread {
    public void run() {
        // Code
    }
}
MyThread t = new MyThread();
t.start();

// Method 2: Implement Runnable
class MyRunnable implements Runnable {
    public void run() {
        // Code
    }
}
Thread t = new Thread(new MyRunnable());
t.start();

// Lambda
Thread t = new Thread(() -> {
    // Code
});
t.start();

// Thread methods
Thread.sleep(1000);
t.join();
t.setPriority(Thread.MAX_PRIORITY);

Exam Tips

Remember:

  1. Thread = lightweight subprocess
  2. Two ways: extends Thread, implements Runnable
  3. start() starts thread, run() contains code
  4. sleep() pauses thread
  5. join() waits for completion
  6. Priority: 1-10, default 5
  7. Main thread always exists
  8. Daemon threads are background
  9. synchronized for thread safety
  10. Runnable preferred over Thread

Common Questions:

  • What is multithreading?
  • Process vs thread?
  • How to create thread?
  • Thread vs Runnable?
  • start() vs run()?
  • Thread lifecycle?
  • Thread methods?
  • Thread priority?
  • What is daemon thread?
  • Synchronization basics?