Multi-threading in Java allows you to execute multiple threads concurrently, enabling your programs to perform multiple tasks simultaneously. This is particularly useful for improving the performance of applications that perform time-consuming operations, such as I/O or complex computations. This tutorial will introduce you to the basics of multi-threading in Java, including creating threads, synchronizing threads, and handling thread communication.
Java Basic Multi-threading Tutorial
By the end of this tutorial, you'll understand how to create and manage threads in Java, making your programs more efficient and responsive.
What is a Thread?
A thread is a lightweight sub-process that runs concurrently with other threads within a single process. Threads share the same memory space, making it easier to share data between them.
- Main Thread: The default thread that starts when a Java program begins execution.
- User-defined Threads: Threads created by the programmer to perform specific tasks.
Creating Threads
There are two main ways to create threads in Java:
- Extending the
Thread
Class:class MyThread extends Thread { public void run() { System.out.println("Thread is running."); } } public class Main { public static void main(String[] args) { MyThread thread = new MyThread(); thread.start(); // Start the thread } }
- Implementing the
Runnable
Interface:class MyRunnable implements Runnable { public void run() { System.out.println("Thread is running."); } } public class Main { public static void main(String[] args) { Thread thread = new Thread(new MyRunnable()); thread.start(); // Start the thread } }
Thread Lifecycle
A thread goes through various states during its lifecycle:
- New: The thread is created but not yet started.
- Runnable: The thread is ready to run and waiting for CPU time.
- Running: The thread is executing its task.
- Blocked/Waiting: The thread is waiting for a resource or another thread.
- Terminated: The thread has completed its task.
Synchronization
When multiple threads access shared resources, synchronization is necessary to prevent data inconsistency. You can use the synchronized
keyword to create synchronized methods or blocks.
- Synchronized Method:
class Counter { private int count = 0; public synchronized void increment() { count++; } public int getCount() { return 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.getCount()); } }
- Synchronized Block:
class Counter { private int count = 0; public void increment() { synchronized (this) { count++; } } public int getCount() { return count; } }
Thread Communication
Threads can communicate with each other using methods like wait()
, notify()
, and notifyAll()
.
- Example:
class SharedResource { private boolean ready = false; public synchronized void produce() { ready = true; notify(); // Notify the waiting thread } public synchronized void consume() { while (!ready) { try { wait(); // Wait until notified } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("Resource is ready."); } } public class Main { public static void main(String[] args) { SharedResource resource = new SharedResource(); Thread producer = new Thread(() -> { resource.produce(); }); Thread consumer = new Thread(() -> { resource.consume(); }); consumer.start(); producer.start(); } }
This tutorial covered the basics of multi-threading in Java. Practice using these concepts to create efficient and responsive applications.