Threading

Native threading constructs for simple asynchronous execution in BoxLang

BoxLang provides native threading constructs for simple asynchronous execution, allowing you to execute code in separate threads. Threads are independent streams of execution that can run simultaneously and asynchronously within a single request.

📋 Table of Contents

🎯 Overview

BoxLang threading allows you to:

✅ Execute code asynchronously in separate threads

✅ Continue request processing without waiting for threads

✅ Wait for threads to complete with join

✅ Pass data to threads via attributes

✅ Control thread priority and lifecycle

✅ Use virtual threads for lightweight concurrency (Java 21+)

Thread Execution Flow

🔀 When to Use Threading vs Async Programming

BoxLang provides two approaches to asynchronous programming:

Feature
Native Threading
Async Programming (BoxFutures)

Complexity

Simple, straightforward

Advanced, composable

Use Case

Fire-and-forget tasks

Chained async operations

Control

Basic (run, join, terminate)

Fine-grained (pipelines, executors)

Composition

Limited

Highly composable

Error Handling

Manual

Built-in exception handling

Return Values

Via thread scope

Direct return values

Best For

Simple parallel tasks

Complex async workflows

Advanced Async Programming: For more control over asynchronous operations, use BoxFutures with runAsync(), completable futures, and executor services. See Asynchronous Programming for details.

⚠️ A Fair Warning About Threading

Thread Safety Requirements

🐛 Thread Debugging

Traditional debugging techniques don't work in threads. Use these approaches:

Debugging Functions

Function
Purpose
Output

println()

Write to console output

Console

writeOutput()

Write to output stream

Output

writedump( output="console" )

Dump complex objects

Console

writeLog()

Write to log files

Log file

Debugging Examples

Debug Output Destinations

📝 Thread Syntax

BoxLang provides multiple syntax options for creating threads:

Script Syntax

Template Syntax

Thread Attributes

Attribute
Type
Required
Default
Description

name

string

Yes (for run)

Auto-generated

Unique identifier for the thread

action

string

No

run

Action to perform: run, join, sleep, terminate

priority

string

No

normal

Thread priority: high, low, normal

duration

integer

Conditional

-

Milliseconds to sleep (required for action="sleep")

timeout

integer

No

-

Milliseconds to wait for join before timeout

virtual

boolean

No

false

Use virtual threads (Java 21+)

* (custom)

any

No

-

Custom attributes passed to attributes scope

🎬 Thread Actions

Action: run (Create and Execute)

Creates a new thread and executes the body asynchronously.

Action: join (Wait for Completion)

Waits for one or more threads to complete before continuing.

Join with Timeout:

Join All Threads:

Action: sleep (Pause Thread)

Pauses the current thread for specified duration.

Action: terminate (Force Stop)

Forcibly terminates a running thread.

🏗️ Complete Thread Examples

Example 1: Fire-and-Forget Background Task

Example 2: Parallel Data Processing

Example 3: Thread with Priority

Example 4: Cooperative Thread Cancellation

🔭 Thread Scopes

BoxLang provides three scopes for managing thread data:

Scope Overview

Scope
Write Access
Read Access
Lifetime
Use Case

Local

Thread only

Thread only

Thread duration

Internal thread variables

Thread

Thread only

All threads + main

Request duration

Share results with main thread

Attributes

None (read-only)

Thread only

Thread duration

Pass input data to thread

1. Thread Local Scope

Variables declared without scope prefix go into the thread's local scope - completely isolated from other threads.

2. Thread Scope (Shared Output)

The thread scope allows threads to write data that the main thread can read.

Writing to Thread Scope (Inside Thread)

Reading from Thread Scope (Outside Thread)

Complete Example

3. Attributes Scope (Input Parameters)

Pass data into threads using attributes - any attribute not recognized as a built-in attribute becomes available in the attributes scope.

Attribute Copy Example

Scope Access Patterns

🧪 Built-In Functions (BIFs)

BoxLang provides several BIFs for thread management:

Thread Management BIFs

BIF
Purpose
Example

threadNew()

Create thread from closure

threadNew( () => processData() )

threadJoin()

Wait for thread completion

threadJoin( "myThread", 5000 )

threadTerminate()

Force stop a thread

threadTerminate( "myThread" )

threadInterrupt()

Signal thread to stop

threadInterrupt( "myThread" )

isInThread()

Check if in thread context

if ( isInThread() ) { ... }

isThreadAlive()

Check if thread is running

if ( isThreadAlive( "myThread" ) ) { ... }

isThreadInterrupted()

Check interrupt status

if ( isThreadInterrupted() ) { break }

threadNew() - Functional Approach

Create threads from closures/lambdas instead of using thread construct:

With Parameters:

threadJoin() - Wait for Completion

isInThread() - Context Detection

isThreadAlive() - Status Check

Cooperative Cancellation Pattern

☁️ Virtual Threads (Java 21+)

BoxLang supports virtual threads (Project Loom) for lightweight, scalable concurrency.

What are Virtual Threads?

Virtual threads are:

  • Lightweight - Millions can run simultaneously

  • Efficient - Lower memory footprint than platform threads

  • Scalable - Perfect for I/O-bound workloads

  • Drop-in - Same API as regular threads

Using Virtual Threads

When to Use Virtual Threads

Use Virtual Threads For:

  • I/O-bound operations (HTTP calls, database queries)

  • High concurrency scenarios (thousands of threads)

  • Blocking operations (file I/O, network)

Don't Use Virtual Threads For:

  • CPU-intensive computations

  • Operations using synchronized blocks heavily

  • Code requiring thread-local storage

Virtual Thread Example

📊 Thread Metadata

Each thread automatically tracks metadata accessible via the thread scope:

Metadata Properties

Property
Type
Description

name

string

Thread name

status

string

NOT_STARTED, RUNNING, COMPLETED, TERMINATED, INTERRUPTED

startTime

datetime

When thread started

endTime

datetime

When thread finished

elapsedTime

numeric

Execution time in milliseconds

priority

string

Thread priority (high, normal, low)

error

struct

Exception details if thread failed

output

string

Buffered output from thread

Accessing Metadata

Error Handling

✅ Best Practices

1. Always Name Your Threads

2. Use Join for Data Dependencies

3. Minimize Shared Data

4. Use Locks for Shared Resources

5. Always Handle Exceptions

6. Clean Up Resources

7. Use Timeouts for Joins

⚠️ Common Pitfalls

1. ❌ Race Conditions on Shared State

2. ❌ Forgetting Deep Copy of Attributes

3. ❌ Using writedump() + abort()

4. ❌ Ignoring Thread Safety

5. ❌ Thread Scope Memory Leaks

🔗 Next Steps: Advanced Async Programming

Native threading is great for simple parallelism, but for advanced async workflows, explore:

🚀 BoxFutures & Async Programming

For complex async operations, use the Asynchronous Programming framework:

  • runAsync() - Return-value based async with CompletableFutures

  • Async Pipelines - Chain async operations with then(), thenRun(), thenCompose()

  • Parallel Collections - array.parallel(), struct.parallel()

  • Executor Services - Custom thread pools with executorNew()

  • Advanced Error Handling - exceptionally(), whenComplete()

  • Async Composition - allOf(), anyOf(), race()

Learn More:

Last updated

Was this helpful?