4.1 Threads Explained
Threads are a fundamental concept in Java programming that allow multiple tasks to be executed concurrently within a single program. Understanding threads is crucial for developing efficient and responsive applications, especially in scenarios involving I/O operations, user interfaces, and complex computations.
Key Concepts
1. Thread Creation
Threads can be created in Java by either extending the Thread
class or implementing the Runnable
interface. The Thread
class provides methods to control the thread's lifecycle, while the Runnable
interface allows for more flexible and reusable code.
Example
// Using Thread class class MyThread extends Thread { public void run() { System.out.println("Thread is running"); } } // Using Runnable interface class MyRunnable implements Runnable { public void run() { System.out.println("Runnable is running"); } } public class Main { public static void main(String[] args) { MyThread thread = new MyThread(); thread.start(); MyRunnable runnable = new MyRunnable(); Thread runnableThread = new Thread(runnable); runnableThread.start(); } }
2. Thread Lifecycle
A thread in Java goes through various states during its lifecycle: NEW
, RUNNABLE
, BLOCKED
, WAITING
, TIMED_WAITING
, and TERMINATED
. Understanding these states helps in managing thread execution and handling potential issues like deadlocks and race conditions.
Example
Thread thread = new Thread(() -> { try { Thread.sleep(1000); System.out.println("Thread is running"); } catch (InterruptedException e) { e.printStackTrace(); } }); System.out.println("Thread state: " + thread.getState()); // NEW thread.start(); System.out.println("Thread state: " + thread.getState()); // RUNNABLE thread.join(); System.out.println("Thread state: " + thread.getState()); // TERMINATED
3. Synchronization
Synchronization is used to control access to shared resources in a multi-threaded environment. Java provides the synchronized
keyword to create synchronized blocks and methods, ensuring that only one thread can execute the synchronized code at a time.
Example
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) throws InterruptedException { Counter counter = new Counter(); Thread thread1 = new Thread(() -> { for (int i = 0; i < 1000; i++) { counter.increment(); } }); Thread thread2 = new Thread(() -> { for (int i = 0; i < 1000; i++) { counter.increment(); } }); thread1.start(); thread2.start(); thread1.join(); thread2.join(); System.out.println("Count: " + counter.getCount()); // Output: 2000 } }
4. Thread Communication
Thread communication is essential for coordinating the execution of multiple threads. Java provides methods like wait()
, notify()
, and notifyAll()
to facilitate communication between threads, ensuring that they can work together efficiently.
Example
class Message { private String msg; private boolean empty = true; public synchronized String read() { while (empty) { try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } empty = true; notifyAll(); return msg; } public synchronized void write(String msg) { while (!empty) { try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } empty = false; this.msg = msg; notifyAll(); } } public class Main { public static void main(String[] args) { Message message = new Message(); Thread writer = new Thread(() -> { String[] messages = {"Hello", "World", "Java"}; for (String msg : messages) { message.write(msg); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } }); Thread reader = new Thread(() -> { for (int i = 0; i < 3; i++) { System.out.println(message.read()); } }); writer.start(); reader.start(); } }
Examples and Analogies
Think of threads as workers in a factory. Each worker (thread) can perform a specific task independently. When multiple workers (threads) are working simultaneously, the factory (program) can produce goods (results) more efficiently. However, if workers need to share resources (synchronization), they must coordinate their actions to avoid conflicts (race conditions). Communication between workers (thread communication) ensures that they can work together smoothly to achieve the desired outcome.
By mastering threads, you can develop more efficient and responsive Java applications, making your programs more capable of handling complex tasks and improving overall performance.