Introduction to Java Threads and Concurrency
Java provides robust support for multithreading and concurrency, which allows you to execute multiple tasks simultaneously. Understanding threads and concurrency is essential for building efficient and responsive applications. This blog will introduce you to the basics of Java threads and concurrency, making it easy for beginners to grasp these concepts.
1. What is a Thread?
A thread is a lightweight process that can run concurrently with other threads within the same program. Each thread has its own execution path but shares the same memory space with other threads in the process.
Example:
Imagine you're baking a cake and preparing coffee at the same time. Both tasks run concurrently but independently, just like threads in a program.
2. Creating Threads in Java
In Java, you can create threads in two main ways:
- By extending the `Thread` class.
- By implementing the `Runnable` interface.
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) {
MyRunnable myRunnable = new MyRunnable();
Thread thread = new Thread(myRunnable);
thread.start(); // Start the thread
}
}
3. Thread Lifecycle
A thread goes through several states during its lifecycle:
1. New: When a thread is created but not yet started.
2. Runnable: When a thread is ready to run but waiting for CPU time.
3. Blocked: When a thread is waiting for a monitor lock to enter or re-enter a synchronized block/method.
4. Waiting: When a thread is waiting indefinitely for another thread to perform a particular action.
5. Timed Waiting: When a thread is waiting for another thread to perform an action within a specified waiting time.
6.Terminated: When a thread has finished its execution.
4. Synchronization
When multiple threads access shared resources, such as variables or objects, they can interfere with each other, causing inconsistent results. Synchronization ensures that only one thread can access a shared resource at a time, maintaining consistency.
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(); // Wait for t1 to finish
t2.join(); // Wait for t2 to finish
} 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;
}
}
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(); // Wait for t1 to finish
t2.join(); // Wait for t2 to finish
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Count: " + counter.getCount());
}
}
5. Inter-Thread Communication
Java provides mechanisms for threads to communicate with each other, primarily through `wait()`, `notify()`, and `notifyAll()` methods. These methods must be called within a synchronized context.
Example
class SharedResource {
private boolean isAvailable = false;
public synchronized void produce() throws InterruptedException {
while (isAvailable) {
wait();
}
System.out.println("Produced");
isAvailable = true;
notify();
}
public synchronized void consume() throws InterruptedException {
while (!isAvailable) {
wait();
}
System.out.println("Consumed");
isAvailable = false;
notify();
}
}
public class Main {
public static void main(String[] args) {
SharedResource resource = new SharedResource();
Thread producer = new Thread(() -> {
try {
for (int i = 0; i < 5; i++) {
resource.produce();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
});
Thread consumer = new Thread(() -> {
try {
for (int i = 0; i < 5; i++) {
resource.consume();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
});
producer.start();
consumer.start();
}
}
6. Thread Pools
Thread pools are used to manage a pool of worker threads efficiently. Java provides the `ExecutorService` framework to create and manage thread pools.
Example:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Main {
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(2);
Runnable task1 = () -> {
System.out.println("Task 1 executed");
};
Runnable task2 = () -> {
System.out.println("Task 2 executed");
};
executorService.execute(task1);
executorService.execute(task2);
executorService.shutdown(); // Shut down the executor service
}
}
Conclusion
Understanding threads and concurrency is crucial for building efficient and responsive Java applications. By mastering thread creation, synchronization, inter-thread communication, and thread pools, you can develop robust multi-threaded programs. Keep practicing these concepts, and you'll become proficient in managing concurrency in Java. Stay tuned for more blogs on advanced Java topics!
Comments
Post a Comment