DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones AWS Cloud
by AWS Developer Relations
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones
AWS Cloud
by AWS Developer Relations
  1. DZone
  2. Coding
  3. Frameworks
  4. A Coroutines Framework for Java

A Coroutines Framework for Java

Check out this tutorial on using coroutines in Java where we focus on two main applications: declarations and executions.

Elmar Sonnenschein user avatar by
Elmar Sonnenschein
·
Nov. 13, 18 · Tutorial
Like (22)
Save
Tweet
Share
37.26K Views

Join the DZone community and get the full member experience.

Join For Free

Inspired by coroutines in Go and Kotlin, we provide a new Java framework for suspending coroutines. Other than existing Java solutions, it is implemented as a simple dependency without using JNI or the need to perform bytecode manipulation from separate Java Agents. It also applies the pattern of structured concurrency, which has been inspired by the noteworthy essay: Notes on structured concurrency, or: Go statement considered harmful by Nathaniel J. Smith. The project is available on GitHub under the Apache 2.0 license and the documentation of the usage and features can be found on the project page. An extended introductory article is also available here.

Because Java doesn't have language support for coroutines, the declaration of coroutines needs to be done through an API. Fortunately, the functional language features available since Java 8, like lambda expressions and method references, together with static imports, allow a concise declaration and execution of coroutines. The underlying implementation is based on Java's CompletableFuture and standard thread pools. Even if a suspension is not needed, the framework can be used as a simpler and structured interface to concurrency.

The application of coroutines is split into two parts: declaration and execution. The declaration is done through a simple builder pattern that composes coroutine from single steps. Such steps can either be simple code executions or suspending functionality, like waiting for I/O to complete or communication with other coroutines that run in parallel. The framework contains several standard coroutine step implementations, including the execution of functional expressions (like lambdas). The following shows a simple coroutine example:

import static de.esoco.coroutine.Coroutine.*;
import static de.esoco.coroutine.step.CodeExecution.*;

Coroutine<String, Integer> parseInteger = first(apply(String::trim))
                                          .then(apply(s -> Integer.valueOf(s)));


This also demonstrates how static imports can be used to simplify the declaration. first() is a factory method that creates a new coroutine instance. It provides a fluent interface for the declaration but a constructor could also be used. The instance method then()extends a coroutine with another execution step. It should be noted that coroutines are always immutable. Extending them with new steps will always create a new coroutine instance. Coroutines are declared with generic types for their input and output values because they can be used similar to Java's Function interface but in asynchronous executions.

The argument to the coroutine builder methods are instances of CoroutineStep. This can either be a custom implementation or one of the predefined step implementations in the framework. The example above shows the most basic CodeExecution step, which provides factory methods, like apply(Function) for steps, that execute functional interfaces.

The executions of coroutines always occur in a CoroutineScope. Scopes provide the environment for structured concurrency and make sure that a set of concurrent executions is always tracked, including cases where exceptions are thrown. The following code shows the execution of a million coroutines in a scope. Launching that amount of parallel threads would either be much slower or could even cause an out of memory error.

import static de.esoco.coroutine.Coroutine.launch;

Coroutine<?, ?> crunchNumbers =
    first(run(() -> Range.from(1).to(10).forEach(Math::sqrt)));

launch(scope -> {
    for (int i = 0; i < 1_000_000; i++) {
        crunchNumbers.runAsync(scope);
    }
});


Again, a static import is used to allow a short notation for the launching of the scope. The lauch() method creates a new scope and executes a functional interface in it with the scope as it's argument. The lambda expression then runs the coroutine asynchronously in the configured thread pool.

The most important property of the scope is that the code after the launch block will only continue executing after all the launched coroutines have finished execution or thrown an error (in which case the scope will also throw an exception). That makes it impossible to ignore the execution state of coroutines and requires the handling of all errors that might occur.

In some cases, it may be more desirable to have an object to track the execution of a scope instead of blocking the current thread. This is especially the case if the coroutines in a scope are used to generate some result that needs to be processed later. For that purpose, the produce() method can be used instead of launch(). That method returns immediately with an instance of Future that can then be used to track the execution of a scope.

The coroutines framework provides a lot more functionality, like continuations, to track single executions, execution contexts, control structures, suspending coroutine steps for asynchronous I/O and non-blocking communication through channels, and advanced concepts, like selection. 

Java (programming language) Framework Execution (computing)

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • How To Set Up and Run Cypress Test Cases in CI/CD TeamCity
  • 5 Steps for Getting Started in Deep Learning
  • Introduction to Container Orchestration
  • Java Code Review Solution

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: