CompletableFutures
, which provide a more convenient and flexible way to handle asynchronous operations.A thread is the smallest unit of execution in a program. In Java, a thread is represented by the Thread
class. Each thread has its own call stack and can execute code independently.
Asynchronous programming allows a program to continue executing other tasks while waiting for a particular task to complete. This is useful for I/O-bound operations where the program would otherwise be blocked.
class MyThread extends Thread {
@Override
public void run() {
System.out.println("Thread is running: " + Thread.currentThread().getName());
}
}
public class ThreadExample {
public static void main(String[] args) {
MyThread thread = new MyThread();
thread.start();
}
}
class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("Runnable is running: " + Thread.currentThread().getName());
}
}
public class RunnableExample {
public static void main(String[] args) {
MyRunnable runnable = new MyRunnable();
Thread thread = new Thread(runnable);
thread.start();
}
}
When multiple threads access shared resources, there is a risk of data inconsistency. Synchronization is used to ensure that only one thread can access a shared resource at a time.
class Counter {
private int count = 0;
public synchronized void increment() {
count++;
}
public int getCount() {
return count;
}
}
public class SynchronizedExample {
public static void main(String[] args) throws InterruptedException {
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();
t1.join();
t2.join();
System.out.println("Final count: " + counter.getCount());
}
}
The ExecutorService
is a higher-level abstraction for managing threads. It provides a pool of threads and allows you to submit tasks for execution.
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ExecutorServiceExample {
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(2);
executorService.submit(() -> {
System.out.println("Task 1 is running: " + Thread.currentThread().getName());
});
executorService.submit(() -> {
System.out.println("Task 2 is running: " + Thread.currentThread().getName());
});
executorService.shutdown();
}
}
CompletableFuture
is a class introduced in Java 8 that provides a more convenient way to handle asynchronous operations. It allows you to chain multiple asynchronous tasks and handle the results in a more functional way.
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
public class CompletableFutureExample {
public static void main(String[] args) throws ExecutionException, InterruptedException {
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "Hello, World!";
});
String result = future.get();
System.out.println(result);
}
}
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
public class CompletableFutureChainingExample {
public static void main(String[] args) throws ExecutionException, InterruptedException {
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
return "Hello";
}).thenApply(s -> s + ", World!");
String result = future.get();
System.out.println(result);
}
}
ExecutorService
to manage resources efficiently.CompletableFutures
, handle exceptions using methods like exceptionally
or handle
to prevent unexpected behavior.CompletableFutures
provide a more readable and maintainable way to handle the results.Java multithreading is a powerful feature that can greatly enhance the performance and responsiveness of applications. Starting from basic thread creation and management, we have explored more advanced concepts like CompletableFutures
. By understanding these concepts and following best practices, you can write more efficient and reliable multithreaded Java programs.