Could Virtual Threads Cast Away The Usage of Kotlin Coroutines
Could Virtual Threads Cast Away The Usage of Kotlin Coroutines
Fetch all data diagram (Horrendous old way to fetch data for Server Side, but great
for mobile … sometimes)
Fetching data with Java Virtual
Threads (Java)
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
var userDeferred = scope.fork(() -> fetchUser(userId));
var postsDeferred = scope.fork(() -> fetchUserPosts(userId));
var commentsDeferred = scope.fork(() -> fetchUserComments(userId));
scope.join();
scope.throwIfFailed();
var processedData = processUserData(
userDeferred.get(), postsDeferred.get(), commentsDeferred.get());
updateUI(processedData);
logger.info(() -> "Complete!");
} catch (ExecutionException e) {
Highly
throw new RuntimeException(e);
} catch (InterruptedException e) { verbose!
throw new RuntimeException(e);
} This is blocking! Alternative:
Thread.startVirtualThread ?
CoroutineScope(IO).launch {
val userDeferred = async { fetchUser(userId) }
val postsDeferred = async { fetchUserPosts(userId) }
val commentsDeferred = async { fetchUserComments(userId) } Is this not
val user = userDeferred.await() verbose? 🤔
val posts = postsDeferred.await() It is
val comments = commentsDeferred.await() better! 😀
val processedData = processUserData(user, posts, comments)
updateSytem(processedData)
logger.info("Complete!")
}
Not blocking! 😀
Send messages diagram (N user messages, packed in 50 messages sent in parallel - Fire
and forget)
Sending Messages
async {
retry(5, 500) { Needs a
sendEmail(text, it) coroutine
}
scope!
}
}.awaitAll()
}
}
compose 7.
8.
expose:
- 8080
9. deploy:
10. resources:
locust -f locust_fulfilment.py -- 11. limits:
host=http://localhost:8080 12. cpus: 0.5
13. memory: 270M
Python code to make tests
from locust import HttpUser, TaskSet, task, between
class UserBehavior(TaskSet):
URL
+
@task
def get_fulfilment(self): Header
url = "/api/v1/fulfilment"
params = {} s
headers = {"Accept": "application/json"}
class WebsiteUser(HttpUser):
tasks = [UserBehavior]
wait_time = between(1, 3)
def on_start(self):
pass
def on_stop(self):
pass
@RestController
@RequestMapping("fulfilment")
This was
class FulfilmentController {
the old
@GetMapping way.
fun getItems() = (1..10).map { Or is it?
val product = Product(name = "TV", isleType = Room) 🤔
logger.info("Product: $product")
product.name
}
companion object {
val logger: Logger = LoggerFactory.getLogger(FulfilmentController::class.java)
}
}
Locust test - Max 100000 users ; Spawn Rate 1000 users/second (~20 seconds = 20000+
simultaneously - MVC)
● Easy pattern to learn
● Good 3 tier model
● Model, View, Controller
● Domain model
Traditional
● Data transfer object
● Converters
Great pattern, but not that ● Not many were aware of the inefficient aspect
of it
capable. Not “fast” enough. ● The inefficiency only appeared when raising
the load
companion object {
val logger: Logger = LoggerFactory.getLogger(FulfilmentController::class.java)
}
}
Locust test - Max 100000 users ; Spawn Rate 1000 users/second (27 seconds = 27000 +
simultaneously - WebFlux)
● Blockhound
Reactive ●
●
Flux / Mono
Flux were used for collections
Programming
● Mono used for instances
● Good support for R2DBC
● Good support for Spring
● The Observability pattern and publishers were
visible
Problem?
Kotlin ●
●
The rise of the usage of Channels, hot flows,
cold flows
Talks about removing Kafka, RabbitMQ and
Problem?
# Server
server.port=8080
spring.main.web-application-type=servlet We can
spring.mvc.servlet.path=/api/v1
activate
# Enable Virtual Threads for WebFlux
virtual threads
spring.threads.virtual.enabled=true
# Thread pool configuration
#spring.task.execution.pool.max-size=16
#spring.task.execution.pool.queue-capacity=100
#spring.task.execution.pool.keep-alive=10s
Virtual Threads in Spring
@RestController
@RequestMapping("fulfilment") It’s back!
class FulfilmentController {
And now
@GetMapping
we are
fun getItems() = (1..10).map { back to
val product = Product(name = "TV", isleType = Room) the old
logger.info("Product: $product") model!😁
product.name
}
companion object {
val logger: Logger = LoggerFactory.getLogger(FulfilmentController::class.java)
}
}
Locust test - Max 100000 users ; Spawn Rate 1000 users/second
● Turns everything on its head for Spring
● There is no keyword or special objects needed
● The application stays reactive
● The preemptive/non-preemptive (hybrid)
scheduling form makes sure that we can
virtualize threads in a code that goes back to
Virtual ●
the imperative form, without the need to be
forced to make major decisions
The use of scopes is off the table and not
Threads needed
Problem?
@GetMapping
fun getItems(): Flow<String> = (1..10).asFlow().map {
An odd
val product = Product(name = "TV", isleType = Room) constructio
logger.info("Product: $product") n, but does
product.name it stand?🤔
}.flowOn(
Executors.newVirtualThreadPerTaskExecutor()
.asCoroutineDispatcher()
)
companion object {
val logger: Logger = LoggerFactory.getLogger(FulfilmentController::class.java)
}
}
Locust test - Max 100000 users ; Spawn Rate 1000 users/second
Virtual Threads
● A different perspective on Threads with
Combined potential benefits
● I never used it in production
With Kotlin ●
●
Maybe it is not an improvement.
Again, testing is needed and lot of
Coroutines benchmarking is needed in order to make
adequate decisions
➔ Java Virtual Threads have a definite future, but for Android at the moment, only up
to JDK17 is supported and no support is available for Java Virtual Threads as a
result.
➔ Kotlin Coroutines will stick around for Android for a long time.
➔ In Java Server Side the use of Kotlin Coroutines may go down for simple
applications and only where certain specific operations are needed will Kotlin
coroutines still likely be needed due to their simplicity.
➔ For all operations related to structured concurrency, streams and event handling,
the usage of Kotlin Coroutines may actually increase as is the case with Mobile
Applications in Android.
➔ Maybe coroutines can one day not be used anymore given the potential of Java
Virtual Threads even in the case of Android applications, but that isn’t anything
Questions?
I am an
inquisitive cat
Resources
Online
● https://kotlinlang.org/docs/coroutines-basics.html
● https://openjdk.org/jeps/444
● https://openjdk.org/projects/jdk/21/
● https://www.jetbrains.com/help/idea/supported-java-versions.html
● https://discuss.kotlinlang.org/t/coroutines-java-virtual-threads-and-scoped-values/28004/1
● https://developer.android.com/build/jdks
Slides available:
● https://www.scribd.com/presentation/768072685/Could-Virtual-Threads-Cast-Away-the-Usage-
of-Kotlin-Coroutines
● https://www.slideshare.net/slideshow/could-virtual-threads-cast-away-the-usage-of-kotlin-coro
utines/271727680
Source code and documentation
● Homepage - https://joaofilipesabinoesperancinha.nl
● LinkedIn - https://www.linkedin.com/in/joaoesperancinha/
● YouTube - JESPROTECH
■ https://www.youtube.com/channel/UCzS_JK7QsZ7ZH-zTc5kBX_g
■ https://www.youtube.com/@jesprotech
● Bluesky - https://bsky.app/profile/jesperancinha.bsky.social
● Mastodon - https://masto.ai/@jesperancinha
● GitHub - https://github.com/jesperancinha
● Hackernoon - https://hackernoon.com/u/jesperancinha
● DevTO - https://dev.to/jofisaes
● Medium - https://medium.com/@jofisaes