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:
r
of typeRunnable
, which represents the task that has completed.t
of typeThrowable
, which is the exception that caused the termination, ornull
if 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.
Comments
Post a Comment
Leave Comment