🎓 Top 15 Udemy Courses (80-90% Discount): My Udemy Courses - Ramesh Fadatare — All my Udemy courses are real-time and project oriented courses.
▶️ Subscribe to My YouTube Channel (178K+ subscribers): Java Guides on YouTube
▶️ For AI, ChatGPT, Web, Tech, and Generative AI, subscribe to another channel: Ramesh Fadatare on YouTube
The ThreadPoolExecutor class in Java provides the afterExecute() method, which can be overridden to perform custom actions after the execution of each task. This guide will cover the usage of the afterExecute() method, explain how it works, and provide concise examples to demonstrate its functionality in real-world use cases.
Introduction
The afterExecute() method is a protected method of the ThreadPoolExecutor class that can be overridden to define custom behavior that should occur after the execution of each task. This can be useful for logging, cleanup, error handling, or other post-processing activities.
afterExecute Method Syntax
The syntax for the afterExecute method is as follows:
protected void afterExecute(Runnable r, Throwable t)
- The method takes two parameters:
rof typeRunnable, which represents the task that has completed.tof typeThrowable, which is the exception that caused the termination, ornullif the execution completed normally.
Examples
Example 1: Logging Task Completion
In this example, we override the afterExecute() method to log the completion of each task.
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class LoggingThreadPoolExecutor extends ThreadPoolExecutor {
public LoggingThreadPoolExecutor() {
super(2, 4, 10, TimeUnit.SECONDS, Executors.newFixedThreadPool(4).getQueue());
}
@Override
protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t);
System.out.println("Task completed: " + r.toString());
if (t != null) {
System.err.println("Task encountered an error: " + t.getMessage());
}
}
public static void main(String[] args) {
LoggingThreadPoolExecutor executor = new LoggingThreadPoolExecutor();
for (int i = 0; i < 5; i++) {
final int taskNumber = i + 1;
executor.submit(() -> {
System.out.println("Executing task " + taskNumber + " by " + Thread.currentThread().getName());
try {
Thread.sleep(1000); // Simulate task execution
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
executor.shutdown();
}
}
Output:
Executing task 1 by pool-1-thread-1
Executing task 2 by pool-1-thread-2
Executing task 3 by pool-1-thread-1
Executing task 4 by pool-1-thread-2
Executing task 5 by pool-1-thread-1
Task completed: java.util.concurrent.FutureTask@<hashcode>
Task completed: java.util.concurrent.FutureTask@<hashcode>
Task completed: java.util.concurrent.FutureTask@<hashcode>
Task completed: java.util.concurrent.FutureTask@<hashcode>
Task completed: java.util.concurrent.FutureTask@<hashcode>
Example 2: Handling Task Exceptions
In this example, we override the afterExecute() method to log any exceptions thrown by the tasks.
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class ExceptionHandlingThreadPoolExecutor extends ThreadPoolExecutor {
public ExceptionHandlingThreadPoolExecutor() {
super(2, 4, 10, TimeUnit.SECONDS, Executors.newFixedThreadPool(4).getQueue());
}
@Override
protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t);
if (t != null) {
System.err.println("Task encountered an error: " + t.getMessage());
} else {
System.out.println("Task completed successfully: " + r.toString());
}
}
public static void main(String[] args) {
ExceptionHandlingThreadPoolExecutor executor = new ExceptionHandlingThreadPoolExecutor();
for (int i = 0; i < 5; i++) {
final int taskNumber = i + 1;
executor.submit(() -> {
System.out.println("Executing task " + taskNumber + " by " + Thread.currentThread().getName());
if (taskNumber == 3) {
throw new RuntimeException("Exception in task " + taskNumber);
}
try {
Thread.sleep(1000); // Simulate task execution
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
executor.shutdown();
}
}
Output:
Executing task 1 by pool-1-thread-1
Executing task 2 by pool-1-thread-2
Executing task 3 by pool-1-thread-1
Executing task 4 by pool-1-thread-2
Executing task 5 by pool-1-thread-1
Task completed successfully: java.util.concurrent.FutureTask@<hashcode>
Task completed successfully: java.util.concurrent.FutureTask@<hashcode>
Task encountered an error: Exception in task 3
Task completed successfully: java.util.concurrent.FutureTask@<hashcode>
Task completed successfully: java.util.concurrent.FutureTask@<hashcode>
Example 3: Custom Task Completion Logic
In this example, we override the afterExecute() method to implement custom logic after the completion of each task.
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class CustomLogicThreadPoolExecutor extends ThreadPoolExecutor {
public CustomLogicThreadPoolExecutor() {
super(2, 4, 10, TimeUnit.SECONDS, Executors.newFixedThreadPool(4).getQueue());
}
@Override
protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t);
if (t == null) {
System.out.println("Task completed: " + r.toString());
// Custom logic after task completion
System.out.println("Performing custom logic after task completion.");
} else {
System.err.println("Task encountered an error: " + t.getMessage());
}
}
public static void main(String[] args) {
CustomLogicThreadPoolExecutor executor = new CustomLogicThreadPoolExecutor();
for (int i = 0; i < 5; i++) {
final int taskNumber = i + 1;
executor.submit(() -> {
System.out.println("Executing task " + taskNumber + " by " + Thread.currentThread().getName());
try {
Thread.sleep(1000); // Simulate task execution
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
executor.shutdown();
}
}
Output:
Executing task 1 by pool-1-thread-1
Executing task 2 by pool-1-thread-2
Executing task 3 by pool-1-thread-1
Executing task 4 by pool-1-thread-2
Executing task 5 by pool-1-thread-1
Task completed: java.util.concurrent.FutureTask@<hashcode>
Performing custom logic after task completion.
Task completed: java.util.concurrent.FutureTask@<hashcode>
Performing custom logic after task completion.
Task completed: java.util.concurrent.FutureTask@<hashcode>
Performing custom logic after task completion.
Task completed: java.util.concurrent.FutureTask@<hashcode>
Performing custom logic after task completion.
Task completed: java.util.concurrent.FutureTask@<hashcode>
Performing custom logic after task completion.
Conclusion
The ThreadPoolExecutor.afterExecute() method in Java provides a way to define custom behavior after the execution of each task. By overriding this method, you can implement logging, error handling, custom logic, or any other post-processing activities.
My Top and Bestseller Udemy Courses. The sale is going on with a 70 - 80% discount. The discount coupon has been added to each course below:
Build REST APIs with Spring Boot 4, Spring Security 7, and JWT
[NEW] Learn Apache Maven with IntelliJ IDEA and Java 25
ChatGPT + Generative AI + Prompt Engineering for Beginners
Spring 7 and Spring Boot 4 for Beginners (Includes 8 Projects)
Available in Udemy for Business
Building Real-Time REST APIs with Spring Boot - Blog App
Available in Udemy for Business
Building Microservices with Spring Boot and Spring Cloud
Available in Udemy for Business
Java Full-Stack Developer Course with Spring Boot and React JS
Available in Udemy for Business
Build 5 Spring Boot Projects with Java: Line-by-Line Coding
Testing Spring Boot Application with JUnit and Mockito
Available in Udemy for Business
Spring Boot Thymeleaf Real-Time Web Application - Blog App
Available in Udemy for Business
Master Spring Data JPA with Hibernate
Available in Udemy for Business
Spring Boot + Apache Kafka Course - The Practical Guide
Available in Udemy for Business
Comments
Post a Comment
Leave Comment