Executors
Powerful Concurrency Made Simple
What Are Executors?
Executors in Java (and BoxLang) are high-level abstractions for managing and controlling thread execution. They provide a powerful way to handle concurrent tasks without the complexity of manually managing threads. Think of executors as specialized worker pools that can handle different types of workloads efficiently.
Tasks Queue β Executor Pool β Results
π π π π π π β
β
β
Here is a great video you can check out about Executors: https://www.youtube.com/watch?v=6Oo-9Can3H8&t=2s
π‘ Why Use Executors?
Thread Management: Automatic creation, pooling, and lifecycle management
Resource Control: Limit concurrent threads to prevent system overload
Task Queuing: Handle more tasks than available threads
Graceful Shutdown: Clean termination of running tasks
Statistical Monitoring: Real-time insights into executor performance
Error Handling: Centralized exception management and logging
Executor Types & Use Cases
Here are all the executors types available in BoxLang, each tailored for specific workloads and performance characteristics:
π CACHED
Best for: Short-lived, asynchronous tasks with unpredictable load
Behavior: Creates threads on demand, reuses idle threads for 60 seconds
Thread Pool: Unbounded, grows and shrinks dynamically
Use Case: Web requests, quick computations, burst I/O operations
Performance: Excellent for variable workloads, can consume many resources
π FIXED
Best for: Consistent workload with known thread requirements
Behavior: Maintains a fixed number of threads throughout lifecycle
Thread Pool: Fixed size, queues tasks when all threads busy
Use Case: CPU-intensive tasks, batch processing, controlled concurrency
Performance: Predictable resource usage, prevents thread explosion
πΏ VIRTUAL (Java 19+)
Best for: I/O-bound tasks requiring massive concurrency
Behavior: Lightweight virtual threads managed by the JVM
Thread Pool: Virtually unlimited, extremely low memory footprint
Use Case: Database calls, HTTP requests, file operations, microservices
Performance: Scales to millions of threads, perfect for I/O blocking
β‘ WORK_STEALING
Best for: Recursive, divide-and-conquer algorithms with uneven workload
Behavior: Threads steal work from each other's deques for load balancing
Thread Pool: Dynamic work distribution, optimal CPU utilization
Use Case: Parallel streams, recursive computations, map-reduce operations
Performance: Excellent for tasks with varying execution times
π΄ FORK_JOIN
Best for: Parallel decomposition of large computational tasks
Behavior: Optimized for fork-join pattern with work-stealing
Thread Pool: Specialized for divide-and-conquer algorithms
Use Case: Parallel algorithms, mathematical computations, data processing
Performance: Superior for CPU-bound recursive tasks
β° SCHEDULED
Best for: Time-based task execution and periodic operations
Behavior: Supports delays, fixed-rate, and fixed-delay scheduling
Thread Pool: Fixed size with timing capabilities
Use Case: Cron jobs, periodic cleanup, delayed tasks, heartbeats
Performance: Efficient for time-sensitive operations
1οΈβ£ SINGLE
Best for: Sequential task execution ensuring order
Behavior: One thread processes tasks in FIFO order
Thread Pool: Single thread, guarantees execution order
Use Case: Order-dependent operations, logging, state management
Performance: No concurrency overhead, thread-safe by design
π§ Pre-defined Runtime Executors
BoxLang ships with three carefully curated executors ready for immediate use. They are defined in boxlang.json in the BoxLang's Home config folder. Check out the configuration section for more information.
It's up to you to create additional executors as needed, but these three cover the most common use cases.
π io-tasks
Type: VIRTUAL
Configuration: Unlimited virtual threads
Purpose: Unlimited I/O bound tasks using Java Virtual Threads
Perfect for: Database queries, API calls, file operations, network requests
Default: Used when requesting executor via
executorGet()Memory: Extremely low per-thread overhead (~KB vs MB for platform threads)
π₯οΈ cpu-tasks
Type: SCHEDULED (20 threads)
Configuration: Fixed 20-thread pool with scheduling capabilities
Purpose: CPU bound tasks with optional scheduling support
Perfect for: Heavy computations, data processing, algorithms, image processing
Scheduling: Supports delayed and periodic execution
β±οΈ scheduled-tasks
Type: SCHEDULED (20 threads)
Configuration: Dedicated 20-thread pool for runtime scheduling
Purpose: All scheduled tasks in the runtime
Perfect for: Cron jobs, periodic maintenance, cleanup tasks, monitoring
Reserved: Used internally by BoxLang runtime for system tasks
π‘ Pro Tip: These executors are automatically available at runtime startup. You can access them immediately without any setup!
π Async Logging
BoxLang provides dedicated logging for all asynchronous operations through the async.log file located in the logs folder of your BoxLang home directory. Please leverage logging as much as possible, as in asynchronous environments logging is critical for debugging and monitoring executor behavior.
Automatic Logging
All executor operations are automatically logged:
Executor creation and configuration
Task submissions and completions
Shutdown events and timing
Error conditions and exceptions
Performance warnings
Manual Logging
You can send custom messages to the async log:
Available Log Types:
"Information"- General operational messages - (Default)"Warning"- Performance issues or concerns"Error"- Execution failures and exceptions"Debug"- Development and troubleshooting info"Trace"- Detailed execution flow information
π Log File Location
Monitor this file for:
Executor performance issues
Task execution failures
Resource exhaustion warnings
Shutdown timing problems
π§ AsyncService
The AsyncService is BoxLang's central service for managing executors and anything async related. It provides a unified API to create, retrieve, and control executors programmatically. This API can be available to you or module authors via the getBoxRuntime().getAsyncService() method, but we would highly suggest also using the global built-in functions (BIFs) for convenience.
newExecutor( name, type, threads )
Create new executors with custom configurations
BoxExecutor
getExecutor( name )
Retrieve executor instances by name
BoxExecutor
hasExecutor( name )
Check if an executor exists
Boolean
deleteExecutor( name )
Remove and shutdown executors
AsyncService
shutdownExecutor( name, force, timeout, unit )
Gracefully shutdown specific executors
AsyncService
shutdownAllExecutors( force, timeout, unit )
Shutdown all registered executors
AsyncService
getExecutorStatusMap()
Get detailed statistics for all executors
IStruct
getExecutorStatusMap( name )
Get statistics for specific executor
IStruct
getExecutorNames()
List all registered executor names
List
Convenience Builder Methods
All methods return a BoxExecutor instance, which is a wrapper around the Java ExecutorService class providing additional features like statistics, health monitoring, activity tracking, logging, and task management.
ποΈ BoxExecutor: Enhanced Executor Management
Important: BoxLang doesn't return raw Java executors. Instead, you get BoxExecutor instances - enhanced wrappers that provide additional functionality beyond a standard Java ExecutorService instance. These executor instances can be passed around wherever an executor is needed or if you need the raw Java ExecutorService, you can access it via the executor() method on the BoxExecutor.
What is BoxExecutor?
BoxExecutor (formerly ExecutorRecord in versions prior to 1.6.0) is BoxLang's enhanced executor wrapper that provides:
π Real-time Statistics: Active threads, completed tasks, pool size metrics, task submission counts
π₯ Health Monitoring: Comprehensive health status tracking with degraded/critical state detection
π Activity Tracking: Last activity timestamps and task submission rate monitoring
π§ Enhanced Control: Graceful and forceful shutdown capabilities
π Integrated Logging: Automatic logging to
async.logβ‘ Convenience Methods: Simplified task submission and result handling
π― Task Factory: Built-in ScheduledTask creation for complex workflows
π State Management: Comprehensive executor state monitoring
β οΈ Health Reports: Detailed health analysis with issues, recommendations, and insights
BoxExecutor Methods
Here are the key methods available on ExecutorRecord instances:
π― Task Submission Methods
submit( callable )
Submit a Callable task for asynchronous execution
Future
Submit and handle result later
submit( runnable )
Submit a Runnable task for asynchronous execution
Future
Fire-and-forget or wait for completion
submitAndGet( callable )
Submit and immediately wait for result
Object
Blocking call, returns result directly
submitAndGet( runnable )
Submit Runnable and wait for completion
Object
Blocking call for side-effect tasks
π§ Lifecycle Management
shutdownQuiet()
Non-blocking shutdown, no new tasks accepted
void
Quick shutdown without waiting
shutdownAndAwaitTermination( timeout, unit )
Graceful shutdown with timeout
void
Recommended for clean shutdown
β οΈ CRITICAL WARNING: Once an executor is shutdown, it CANNOT be restarted! The executor becomes permanently dead. Always plan your shutdown strategy carefully and prefer graceful shutdown with
shutdownAndAwaitTermination()when possible.
π Task Factory Methods
newTask( name )
Create named ScheduledTask bound to this executor
ScheduledTask
For complex scheduling workflows
newTask()
Create auto-named ScheduledTask bound to this executor
ScheduledTask
Quick task creation
π Information & Monitoring
getStats()
Get comprehensive executor statistics with health monitoring
IStruct
Performance monitoring, debugging, health checks
isHealthy()
Quick boolean health check
Boolean
Fast health verification
name()
Get executor name
String
Identification and logging
type()
Get executor type
ExecutorType
Type checking and decisions
maxThreads()
Get maximum thread count
Integer
Capacity planning
executor()
Get underlying Java ExecutorService
ExecutorService
Direct Java interop if needed
isTerminated()
Check if all tasks completed after shutdown
Boolean
Shutdown verification
isTerminating()
Check if executor is shutting down
Boolean
State checking
isShutdown()
Check if executor has been shut down
Boolean
State checking
π― Specialized Methods
scheduledExecutor()
Get as BoxScheduledExecutor
BoxScheduledExecutor
SCHEDULED type only
π BoxExecutor Statistics & Health Monitoring
Every BoxExecutor provides detailed runtime statistics including comprehensive health monitoring introduced in version 1.6.0:
π₯ Health Status Values
The healthStatus field can return the following values:
"healthy"- Executor operating normally within all thresholds"degraded"- Performance issues detected, action recommended"critical"- Serious issues requiring immediate attention"idle"- No recent activity, may be underutilized"shutdown"- Executor has been shut down, no new tasks accepted"terminated"- All tasks completed, executor fully terminated"draining"- Shutting down and processing remaining tasks
π― Health Monitoring Thresholds
BoxExecutor uses configurable thresholds to determine health status:
Pool Utilization
75%
95%
Thread Utilization
75%
95%
Queue Utilization
70%
95%
Task Completion Rate
<50%
<25%
Inactivity
30 minutes
N/A
π Health Report Structure
The health report provides detailed analysis:
π Monitoring Examples
Check overall health:
Monitor specific metrics:
Automated health checks:
π Global BIFs (Built-in Functions)
BoxLang provides convenient global functions for executor management and usage:
executorGet( [name] )
Get BoxExecutor by name (defaults to "io-tasks")
BoxExecutor
executorGet( "cpu-tasks" )
executorHas( name )
Check if executor exists
Boolean
executorHas( "my-pool" )
executorList()
List all executor names
Array
executorList()
executorNew( name, type, [threads] )
Create new executor
BoxExecutor
executorNew( "pool", "fixed", 8 )
executorShutdown( name, [force], [timeout] )
Shutdown executor gracefully or forcefully
Boolean
executorShutdown( "pool", false, 30 )
executorStatus( [name] )
Get executor statistics and status
Struct
executorStatus( "cpu-tasks" )
Remember that if the BIF returns an executor, it will be a BoxExecutor instance, not a raw Java ExecutorService. This allows you to leverage all the enhanced features and methods provided by BoxLang, including health monitoring, activity tracking, and comprehensive statistics.
BIF Usage Examples
β° Scheduled Tasks: Use BoxLang Schedulers
π― Preferred Approach: For scheduled tasks, use BoxLang's dedicated Scheduler framework instead of direct executor scheduling. Schedulers provide more features, better management, and integrated lifecycle handling.
Scheduler vs Direct Executor Scheduling
Benefits of BoxLang Schedulers:
Rich Configuration: Timezone support, complex scheduling patterns
Lifecycle Management: Automatic startup/shutdown handling
Error Handling: Built-in retry logic and error recovery
Monitoring: Enhanced logging and statistics
Persistence: Optional task persistence across restarts
π Practical Examples
π Basic Usage
β‘ Asynchronous Task Execution with Error Handling
β° Advanced Scheduled Task Management
π Parallel Processing with Work Stealing
π Batch Processing with Virtual Threads
β‘ Performance Considerations & Best Practices
π¨ Critical Guidelines:
Virtual Threads: Perfect for I/O, avoid for CPU-intensive tasks
Fixed Pools: Size according to available CPU cores for CPU tasks
Cached Pools: Monitor thread creation, can grow unbounded
Always Shutdown: Clean up custom executors to prevent resource leaks
Monitor Stats: Use
getStats()to track executor performanceLog Everything: Use async logging for troubleshooting and monitoring
Use BoxFutures it is tempting to use the Java Future, but BoxFutures provide better integration with BoxLang's async model and error handling.
Use BoxLang Schedulers: For scheduled tasks, prefer BoxLang's Scheduler framework for better management and features.
π― Executor Selection Matrix
Database Queries
VIRTUAL (io-tasks)
Unlimited
Very Low (~KB/thread)
Excellent for I/O
File Operations
VIRTUAL (io-tasks)
Unlimited
Very Low
Scales to thousands
HTTP API Calls
VIRTUAL (io-tasks)
Unlimited
Very Low
Perfect for microservices
Mathematical Calculations
FIXED (cpu-tasks)
= CPU cores
Medium
Optimal CPU usage
Image/Video Processing
FIXED
= CPU cores
High
Prevents oversubscription
Data Transformations
WORK_STEALING
= CPU cores
Medium
Load balancing
Parallel Algorithms
FORK_JOIN
= CPU cores
Medium
Divide-and-conquer
Periodic Maintenance
SCHEDULED
Small (2-5)
Low
Time-based execution
Background Tasks
CACHED
Dynamic
Variable
Burst workloads
Sequential Processing
SINGLE
1
Low
Order guarantee
π Performance Monitoring
π‘οΈ Error Handling & Recovery
π₯ Production Best Practices
Use Built-in Executors: Start with
io-tasksandcpu-tasksfor most scenariosCreate Custom Sparingly: Only create custom executors for specific performance requirements
Monitor Continuously: Implement regular stats monitoring and alerting
Plan Shutdown Strategy: Always implement graceful shutdown with fallback to forceful
Log Comprehensively: Use async logging for all important operations
Test Under Load: Validate executor behavior under realistic workloads
Size Appropriately: Match thread counts to actual hardware capabilities
π Quick Start Template
This template covers 80% of use cases - simple, effective, and leverages BoxLang's optimized defaults with proper error handling and logging! Please note that if you need fluent pipelines or enhanced computation capabilities, consider using BoxLang's BoxFutures for better integration with the async model and leveraging the full power of CompletableFuture.
Last updated
Was this helpful?
