Skip to content

da0hn/virtual-thread-structured-concurrency-sandbox

Repository files navigation

Java 21 Virtual Threads and Structured Concurrency

Java Threads and Scalability

  • Task types
  • Concurrency and Parallelism
  • Non Blocking IO
  • Introduce Project Loom

Task Types

%%{ init: { 'flowchart': { 'curve': 'cardinal' } } }%%
flowchart LR
    subgraph External World
        browser(("Browser"))
    end
    subgraph Typical Web Application
        application["\nWeb Application\n(Process)\n"]
        database_1[(Database 1)]
        database_2[(Database 2)]
        ms_1{{"Microservice 1\n(Process)"}}
        ms_2{{"Microservice 2\n(Process)"}}

    end
    application --> external_api
    browser -->|User Request| application
    browser -->|User Request| application
    browser -->|User Request| application
    application <--> database_1
    application <--> database_2
    application <--> ms_1
    application <--> ms_2
Loading

Process Per Request (CGI)

flowchart LR
    web-server["Web Server\n(Process)"]
    handler-1["User 1 Handler\nCGI Script\n(Process)"]
    handler-2["User 2 Handler\nCGI Script\n(Process)"]
    handler-3["User 3 Handler\nCGI Script\n(Process)"]
    external-call["\n\n\n\n\n\n       "]
    external-call -->|User 1 Request| web-server
    external-call -->|User 2 Request| web-server
    external-call -->|User 3 Request| web-server
    web-server <-->|" (1) Start Process "| handler-1
    web-server <-->|" (2) Start Process "| handler-2
    web-server <-->|" (3) Start Process "| handler-3
Loading
  • Process is heavyweight
  • Limited number of processes per machine
    • Scalability issues
    • Cannot support large number of users
  • Expensive Process startup and termination time
  • Difficult to share data or communicate between Processes
  • FastCGI
    • Pooling of Processes
    • CGI processes are started upfront for performance

Thread Per Request

flowchart LR
    user-request["\n\n\n\n\n"]
    subgraph web-application["fa:fa-internet-explorer Web Application"]
        thread-1-start(("start"))
        thread-1-finish(("end"))
        thread-2-start(("start"))
        thread-2-finish(("end"))
        thread-3-start(("start"))
        thread-3-finish(("end"))
    end
    user-request -->|User 1 Request| web-application
    user-request -->|User 2 Request| web-application
    user-request -->|User 3 Request| web-application
    thread-1-start -->|" 1. Thread process "| thread-1-finish
    thread-2-start -->|" 2. Thread process "| thread-2-finish
    thread-3-start -->|" 3. Thread process "| thread-3-finish
Loading
  • Threads are lightweight
    • But has its own stack
  • Can handle larger number of concurrent users
  • Can share data or communicate between threads
  • Improved Performance
    • No extra process to deal with
  • Easy to understand
  • Easy to debug

Concurrency and Parallelism

Concurrency

  • Multiple independent tasks are making progress but may not execute at the same time
  • Appearance of simultaneous execution (parallelism)
  • Concurrency is about dealing with lots of things at once
  • CPU time slicing

Parallelism

  • Multiple dependent sub-tasks are executing at the same time
  • Multiple cores needed
  • No parallelism in single core

Synchronous Call

  • Sequential execution of code
  • Easy to understand
  • Easy to debug

Asynchronous Call

  • Does not wait for call to complete
  • Callbacks, Futures...
  • More complex to understand
  • In java, user Threads

Threads and Scalability

  • Default stack size 1M
    • As number of users increase, memory usage increases
  • There is a max limit to the max threads
    • Depends on VM or Machine Memory
    • Much more socket connections can be supported
    • This prevents optimum scalability
  • IO Bound Tasks
    • Paralyzes the OS thread for a longer time than necessary

Scalability Solutions

  • {Optimized Scalable Application} + {Vertial Scaling} + {Horizontal Scaling}

Vertical Scaling

  • Increase resources
  • CPU, Memory, Disk Space, etc...
  • Limit to scaling
  • Increases cost
  • Cloud Environment

Horizontal Scaling

  • Increase number of Application (Instances) nodes
  • No limit
  • Costly

Horizontal Scaling Image

Non Blocking IO

  • Pseudo Code For Blocking IO

Blocking IO program flux

// Fetch some data from DB
var data1 = fetchDataFromDB(dbUrl);

// Fetch some data from Microservice
var data2 = fetchDataFromService(serviceUrl);

// Process all data
var combinedData = processAndCombine(data1, data2);

// Send data to user
sendData(combinedData);

Blocking IO Diagram

Non-Blocking IO program flux

Non-Blocking IO Diagram

// Non Blocking: Fetch some data from DB
return fetchDataFromDb(dbUrl, data1 -> {
    // Non Blocking: Fetch some data from Microservice
    return fetchDataFromService(serviceUrl, data2 -> {
        // Process all data and send
        var combinedData = processAndCombine(data1, data2);
        return sendData(combinedData);
    });
});

Virtual Threads

  • Lightweight threads (Extends the Thread Class)
    • Fast creation time
    • Exhibits same behaviour as Platform Threads
    • Scales to millions of instances

Advantages

  • No need for Thread Pool
  • Can block on IO with no scalability issues
  • Optimal concurrency
  • Code can still be sequential
  • Existing code will benefit from using Virtual Thread
  • Combine with Future's and CompletableFuture's

Limitations

  • Blocking with native frames on Stack (JNI)
    • This is rare
  • Control memory per stack
    • Reduce ThreadLocal's usage
    • No deep recursions

About

Java 21 Virtual Thread and Structured Concurrency Udemy Course

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages