A short and practical intro into project loom

Overview

project-loom

Project loom is all about making concurrency easier (for developers) on the JVM. It is in experimental phase. Download the early access builds here

Concurrency

We can think of concurrency as interleaving of independent tasks. concurrency

In an HTTP server you can think of Request handling as an independent task. Requests are independent of each other but share the resources that are required to execute the task (CPU, IO etc).

Differences in the Unit of Concurrency

  • For a HTTP server the unit of concurrency is the request itself.
  • For the underlying OS the unit of concurrency is the OS thread.
  • Unit of concurrency in the JVM is java.lang.Thread. We will refer to this as JVM Thread

An HTTP server (written with blocking IO), will allocate a JVM Thread to each incoming request from a pool of JVM Threads. The JVM Thread will execute your code sequentially.

Looking back at the differences in unit of concurrency: if we need to increase the throughput, i.e, the number of requests being handled at given point of time, then we need to increase the number of JVM Threads. Sounds reasonable. A million requests handled by a million JVM Threads.

The Problem

The problem with scaling JVM Threads is that JVM Threads are just thin wrappers around OS threads. JVM Thread and OS Thread have a 1:1 mapping. So creating a JVM Thread creates an OS Thread. A few things to know about OS Threads:

  • OS Threads are precious resources. Blocking a thread is is huge waste of this resource.
  • Creating hundreds of thousands of OS threads is not possible.
  • Switching between threads is expensive.

An example:

import scala.util.Try

type UserID = String

case class UserDetails(name: String, phone: String, age: Int)

case class UPIResponse(bankName: String, bankID: String,lastTransactionOn: java.time.Instant)

def fetchUserDetails(userId: UserID): Try[UserDetails] = ???

def fetchUPIDetails(phone: String): Try[UPIResponse] = ???

def isBankPaymentWorking(bankID: String): Try[Boolean] = ???

val userID: UserID = ???

///Here the JVM Thread is an OS Thread

val result: Try[Boolean] = 
  fetchUserDetails(userID) // JVM Thread is blocked
    .flatMap(userDetails => fetchUPIDetails(userDetails.phone)) // JVM Thread is blocked
    .flatMap(upiResponse => isBankPaymentWorking(upiResponse.bankID)) // JVM Thread is blocked

result match {
  case Success(isPaymentWorking) => println(isPaymentWorking)
  case Failure(e) => e.printStackTrace
}


If you look at the the methods fetchUserDetails fetchUPIDetails and isBankPaymentWorking you will notice that the thread is not doing any work there. It is just waiting. This IO is referred to as Blocking IO.

In comes Asynchronous NIO in JDK

To combat the above problems asynchronous apis were introduced in the java.nio package. These go to great lengths to not block threads. The thread is returned to the thread pool (with a callback registered) for the duration of the blocking call (external API or database call). There is an interrupt triggered when response is made available from the external call and the callback is invoked on another thread from the thread pool.

Advantages of Async IO

  • More efficient as we just made sure that we don't block any JVM threads (and in turn OS threads). They are all doing some work
  • We can now handle more requests as opposed to from when we were blocking threads

The Problem

  • Async code is hard to reason about as a developer. Composing functions is not possible with callbacks. A lot of callbacks can lead to "Callback Hell"
  • Loss of context. The thread carrying out the task prior to the IO call and the thread carrying out the task after the IO call are not guaranteed to be the same. A random thread from the thread pool is picked up and assigned to execute the callback code. As the unit of concurrency on the JVM is a JVM Thread, thread locals, exception stack traces are all bound to the thread.

call back hell

In comes Future

To combat the problem of composibility, CompletableFuture was introduced into the JDK. A CompletableFuture<T> will result in value of type T or an error. You could now have lots of futures doing many computations without worrying about hogging the underlying thread pool because of it's async nature.

The problem

  • Though futures solving the problem of composibility, they are still a library construct. That means they will go viral in your code base. All your functions right from the handler to the database layer need to return futures.
  • They still don't solve the problem of Loss of context mentioned above. The exception stacktraces are not useful because the composed futures would all be computed on different threads.

The example below shows Future in Scala which existed before they were introduced in Java.

import scala.concurrent.Future
import scala.util.{Success, Failure}

type UserID = String

case class UserDetails(name: String, phone: String, age: Int)

case class UPIResponse(bankName: String, bankID: String, lastTransactionOn: java.time.Instant)

def fetchUserDetails(userId: UserID): Future[UserDetails] = ???

def fetchUPIDetails(phone: String): Future[UPIResponse] = ???

def isBankPaymentWorking(bankID: String): Future[Boolean] = ???

val userID: UserID = ???

/*
The 3 API calls are sequential but they are not guaranteed to run on the same underlying OS thread.
*/
fetchUserDetails(userID)
  .flatMap(userDetails => fetchUPIDetails(userDetails.phone))
  .flatMap(upiResponse => isBankPaymentWorking(upiResponse.bankID))
  .onComplete {
    case Success(isWorking) => println(s"The bank's payment is working? $isWorking")
    case Failure(e) => e.printStackTrace
  }

In comes Project Loom

Project loom brings in green threads to the JVM. These green threads are called virtual threads. They DO NOT map 1:1 to the OS Threads. So, a programmer is free to create as many virtual threads as they want. We will refer to these threads as Virtual Threads.

The new unit of concurrency on the JVM

Virtual Threads now become the new unit of concurrency in JVM. The developer need not worry about the underlying OS threads. A few things about Virtual Threads:

  • Creating and blocking virtual threads is cheap
  • This is because they do not map 1:1 to OS Threads
  • JVM can support millions of virtual threads, allowing you to map the unit of concurrency of the application domain to the unit of concurrency in the runtime.
  • Allows you to write non-blocking code in the blocking style which preserves readability. The "async" part is just an implementation detail.

Let us execute some code

At the time of writing this readme, the latest jdk version was jdk-17.

The file ThreadDemo.java spins up 25,000 threads.

<path>/jdk-17.jdk/Contents/Home/bin/javac ThreadDemo.java
<path>/jdk-17.jdk/Contents/Home/bin/java ThreadDemo

This most likely will throw a java.lang.OutOfMemoryError saying that it is unable to create a native thread.

The file VirtualThreadDemo.java spins up a million Virtual Threads and the JVM exits smoothly.

<path>/jdk-17.jdk/Contents/Home/bin/javac VirtualThreadDemo.java
<path>/jdk-17.jdk/Contents/Home/bin/java VirtualThreadDemo
You might also like...

Kotlin-decompiled - (Almost) every single language construct of the Kotlin programming language compiled to JVM bytecode and then decompiled to Java again for better readability

Kotlin: Decompiled (Almost) every single language construct of the Kotlin programming language compiled to JVM bytecode and then decompiled to Java ag

Dec 14, 2022

An OpenJDK release maintained and supported by SAP

SapMachine This project contains a downstream version of the OpenJDK project. It is used to build and maintain a SAP supported version of OpenJDK for

Jan 3, 2023

JavaOTTF - Official OTTF parser and composer for JVM languages

JavaOTTF - Official OTTF parser and composer for JVM languages

JavaOTTF Official OTTF parser and composer for JVM languages. Documentation Please refer to the Wiki Section. Installation Maven Add repository into p

Nov 21, 2022

A sidecar to run alongside Trino to gather metrics using the JMX connector and expose them in different formats using Apache velocity

Overview A sidecar to run alongside Trino to gather metrics using the JMX connector and expose them in different formats using Apache Velocity. Click

Nov 18, 2021

Add a partial Coeffect system into Java using Loom's ExtentLocals

Coeffect Add a partial Coeffect system into Java using Loom's ExtentLocals. In Java there are generally 2 strategies to manage the parameters a method

Sep 9, 2022

My first proper GitHub project, I guess. Basically an automated version of the "Battle Royale" short series on Geo Facts' YouTube channel.

My first proper GitHub project, I guess. Basically an automated version of the

State-Royale Made by Pixer415, with some help from ThatOneCalculator This project needs your contributions. New modes/new features/typo fixes/suggesti

Jun 27, 2022

An assistance platform made using Spring framework that analyses your code, and helps you either to start a devops project, or to turn an existing project into a devops project using open source software (Git, Docker, Jenkins..)

An assistance platform made using Spring framework that analyses your code, and helps you either to start a devops project, or to turn an existing project into a devops project using open source software (Git, Docker, Jenkins..)

DevOpsify Description An assistance platform made using Spring framework that analyses your code, and helps you either to start a devops project, or t

Nov 8, 2022

A practical example to showcase Redis Streams and RediSearch in action

A practical example to showcase Redis Streams and RediSearch in action

Redis Streams in Action A practical example to showcase Redis Streams in action. The goal is to process Twitter data in real-time for search and query

Dec 19, 2022

This repo contains all the materials for placement as well as Practical lab codes for all subjects and notes. For students graduating in 2023

UEMK_PLACEMENT_2023 This repo contains all the materials for placement as well as Practical lab codes for all subjects and notes. For students graduat

Mar 5, 2022

Java Practical CO1212 exercises and assignment answers.

CO1212 Exercises Java Practical CO1212 exercises and assignment answers. Contribute to this You can find the questions in their respective directories

Apr 12, 2022

This module explains about the example of Spring MVC + Database Integration with MySQL using Hibernate ORM with practical coding example and required JAR dependencies

SpringMVC-Database-Integration This module explains about the example of Spring MVC + Database Integration with MySQL using Hibernate ORM with practic

Nov 2, 2021

Vert.x PoC for Project Loom Support

Vert.x Loom Wrapper Codegen This project contains a proof of concept implementation for a codegen wrapper API that provides virtual async-await suppor

Oct 10, 2022

Echo client-server components to evaluate Project Loom virtual threads.

Echo client-server components to evaluate Project Loom virtual threads.

Overview Project Loom is the OpenJDK initiative to introduce user-mode threads in Java. The purpose of this repository is to compare Project Loom virt

Nov 1, 2022

The first Java Actor System supporting fibers from Project Loom

Fibry Fibry is an experimental Actor System built to be simple and flexible. Hopefully, it will also be fun to use. Fibry is the first Java Actor Syst

Dec 26, 2022

Experimenting with Project Loom

Project Loom Lab Experiments with Project Loom's features based on these JEP(draft)s: Structured Concurrency Virtual Threads Experiments For these exp

Dec 23, 2022

Async-Await support for Vertx using Project Loom

Vertx-Async-Await Async-Await support for Vertx using Project Loom. import static com.augustnagro.vertx.loom.AsyncAwait.async; import static com.augus

Oct 10, 2022

Async-Await support for Vertx using Project Loom

Vertx-Async-Await Async-Await support for Vertx using Project Loom. import static com.augustnagro.vertx.loom.AsyncAwait.async; import static com.augus

Jun 9, 2022

Samples showing practical aspect of EventStoreDB, Event Sourcing

EventStoreDB samples EventStoreDB is an industrial-strength database technology used as the central data store for event-sourced systems. It is availa

Oct 26, 2022

👄 The most accurate natural language detection library for Java and the JVM, suitable for long and short text alike

👄 The most accurate natural language detection library for Java and the JVM, suitable for long and short text alike

Quick Info this library tries to solve language detection of very short words and phrases, even shorter than tweets makes use of both statistical and

Dec 28, 2022
Owner
Ashwin Bhaskar
Ashwin Bhaskar
Dynamic loading and compiling project based on JVM

camphor 基于jvm的弹性加载及编译中间件(Elastic loading and compiling middleware based on JVM) camphor_0.0.1 项目简介 该项目定位为弹性中间件,能够使系统在不重启的情况下完成增量代码文件的动态编译和加载 模块介绍 camp

palading 1 Jan 22, 2022
Ray Tracing project from students of the ENSEEIHT Engineering School

Ray-Tracing-N7 This is a school project of students of the ENSEEIHT engineering school, in which we will try to recreate a basic ray tracing system, w

Tibo Mousset 3 Jun 23, 2022
Sampling CPU and HEAP profiler for Java featuring AsyncGetCallTrace + perf_events

async-profiler This project is a low overhead sampling profiler for Java that does not suffer from Safepoint bias problem. It features HotSpot-specifi

null 5.8k Jan 3, 2023
Get Method Sampling from Java Flight Recorder Dump and convert to FlameGraph compatible format.

Note: Travis has removed the support for Oracle JDK 8. Therefore the build status is removed temporarily. Converting JFR Method Profiling Samples to F

M. Isuru Tharanga Chrishantha Perera 248 Dec 16, 2022
Log analyser / visualiser for Java HotSpot JIT compiler. Inspect inlining decisions, hot methods, bytecode, and assembly. View results in the JavaFX user interface.

JITWatch Log analyser and visualiser for the HotSpot JIT compiler. Video introduction to JITWatch video Slides from my LJC lightning talk on JITWatch

AdoptOpenJDK 2.8k Jan 3, 2023
Simple JVM Profiler Using StatsD and Other Metrics Backends

statsd-jvm-profiler statsd-jvm-profiler is a JVM agent profiler that sends profiling data to StatsD. Inspired by riemann-jvm-profiler, it was primaril

Etsy, Inc. 330 Oct 30, 2022
Small set of tools for JVM troublshooting, monitoring and profiling.

Swiss Java Knife (SJK) SJK is a command line tool for JVM diagnostic, troubleshooting and profiling. SJK exploits standard diagnostic interfaces of JV

Alexey Ragozin 3.2k Jan 3, 2023
Best-of-breed OpenTracing utilities, instrumentations and extensions

OpenTracing Toolbox OpenTracing Toolbox is a collection of libraries that build on top of OpenTracing and provide extensions and plugins to existing i

Zalando SE 181 Oct 15, 2022
One file java script for visualizing JDK flight recorder execution logs as flamegraphs without any dependencies except Java and a browser.

Flamegraph from JFR logs Simple one file Java script to generate flamegraphs from Java flight recordings without installing Perl and the Brendan Gregg

Billy Sjöberg 17 Oct 2, 2022
Simple Anti-Dump to slow down and annoy attackers.

Anti-Dump A simple Anti-Dump to slow down and annoy attackers. Usage Copy the class into your mod or loader. Rename any instances of dummy/class/path

null 47 Dec 25, 2022