SkillAgentSearch skills...

Fibry

The first Java Actor System supporting fibers from Project Loom

Install / Use

/learn @lucav76/Fibry

README

Fibry

Fibry is an Actor System with Multi Agents capabilities, built to be simple and flexible. Hopefully, it will also be fun to use.
Fibry is the first Java Actor System designed to use Virtual Threads, since 2019, when they were only available from Project Loom and were called Fibers (and hence the project name).
Commercial support, provided by DGTZ AS, is available for each version of Fibry and Java (starting from JDK 8). Please reach out to Luca Venturi here or on LinkedIn to discuss it.
Please note that from version 3.0.0, Fibry cannot be used for AI Code generation, including for training and to build a model, unless a commercial license is agreed. Other AI cases are fine. Please read the license and reach out to Luca Venturi if in doubt.

Fibry 3.X requires JDK 21+, and it is the recommended version, as JDK 21 finally merged virtual threads / fibers into the mainline, so you no longer need a build of Loom to use them.
Fibry 2.X requires JDK 11+, and you need Loom to get access to virtual threads
Fibry 1.X works JDK 8+, and you need Loom to get access to virtual threads
Fibry 2.X is supported, and changes are available in the jdk11 branch.
Fibry 1.X is supported, and changes are available in the jdk8 branch.
Fibry aims to replicate some of the features of the Erlang Actor System in Java.
Fibry allows you to send code to be executed in the thread/fiber of an actor, a mechanism similar to the one used in Chromium and to Java Executors. Fibry is following the "Tip and Tail model" as described by JEP 14, so new features will only be available on version 3.X, and version 1.X and 2.X will only receive critical bug fixes.

The original line of development was meant to make Fibry useful on the creation of IoT products and video games supporting online multi-players functionalities and chats.
However, Fibry proved useful on Big Data projects, helping to move terabytes of data and making scheduled tasks more resilient, thanks to its Auto Healing.
Its generators functionality makes easier to squeeze some performance on special tasks, like cloud buckets traversal.

Simplicity first, flexibility second

Fibry has been designed to be simple yet flexible, easy to add to an existing project:

  • Fibry has no dependencies, so no conflicts, no security issues on the dependencies and no surprises, just a tiny jar available in the Maven Central repository
  • Your actors can and should use synchronous logic
  • You can use both virtual threads (if you run on JDK 21+ or on Loom) and native threads
  • There is a series of Stereotypes to handle common scenarios
  • Your actors don't need to extend any particular class, they can just implement Consumer or Function
  • Your actors have anyway the option to extend CustomActor and CustomActorWithResult, if this suits you best
  • If you choose to simply implements Consumer and Function, your actors can also be used "transparently" in code that knows nothing about Fibry
  • It is simple to retrieve the result of a message
  • It is possible to send messages to named actors even before they are created, potentially simplifying your logic; the messages can be discarded or processed when the actor will be available
  • There is a fluid interface to build the actors
  • On some actors, you can receive messages of your choice while processing a message (Erlang style)
  • Many types of actor implement the Executor interface, so you can "send code" to be executed by the thread of almost any actors, and use them on service that are not actor-aware
  • Most actors can be converted to Reactive Flow Subscribers (TCK tested), calling asReactiveSubscriber()
  • Fibry can create generators (Iterables with back-pressure) in a simple and effective way
  • Remote actors can be discovered using UDP Multicast
  • It implements a way to schedule messages in the future
  • It implements several ways to schedule tasks periodically
  • It implements several types of actor pools, for work-stealing tasks, with the possibility to assign a weight to each job
  • It implements a very simple Map/Reduce mechanism, limited to the local computer.
  • It implements a very simple Pub/Sub mechanism, limited to the local computer.
  • It implements SyncVar and SyncMap, to notify (even remote actors) actors that a variable (or the value of a map) changed
  • It implements a simple TCP port forwarding, both as a Stereotype and as a small cli application: TcpForwarding
  • It implements some simple mechanisms to help to process messages in batches
  • It implements a mechanism to track progress of long-running tasks, which can be extended to support the progress of messages processed by another server
  • It provides a way to create simple Finite State Machines, either with Actors or with Consumers (recommended)
  • It provides support for three types of transactions, from lightweight to full transactions, with roll-back

Some numbers (from Loom)

So, virtual threads (fibers) are faster than threads. Got it. How much faster? Very much. Depending on your problem, you can consider them 10X-100X faster than threads. While Fibry has not been optimized for extreme performance (e.g. it is based on a standard JDK queue), performance has been taken into high consideration, with the result that generally you don't pay the price of features that you don't need, which explains why there are so many types of actors with different capabilities.
Also, at the time of the test Loom was not completed yet, so its performance can be different from now.
I took some informal benchmarks using a C5.2xlarge VM instance, without tuning of the OS or of Loom:

  • Number of concurrent threads that can be created without OS tuning: around 3K
  • The expected maximum with OS tuning: around 33K
  • Number of concurrent fibers that can be created without OS tuning: more than 3M (100x - 1000X better)
  • Threads created per second: 15K
  • Fibers created per second: 600K (40x better)
  • Sync messages per second, between 2 threads (requires thread switching): 50K
  • Sync messages per second, between 2 fibers (requires fiber switching): 150K (3x better)

As an indication, Fibry can send around 7-8M of messages per second from a single core, under low thread contention.

Including Fibry in your projects

You can find Fibry on Maven Central.

To include it using Gradle:

compile group: 'eu.lucaventuri', name: 'fibry', version: '3.0.2'

To include it using Maven:

<dependency>
    <groupId>eu.lucaventuri</groupId>
    <artifactId>fibry</artifactId>
    <version>3.0.2</version>
</dependency>

Why Virtual Threads?

Virtual threads, or fibers, are lightweight threads. Lightweight means that you can have many of them, and in fact Fibry will be happy to keep running several million of virtual threads at the same time, if that's what you need. With threads, depending on your configuration, you can maybe have some tens of thousands.

Surely you can use thread pools, but if you need to execute long operations this can be a problem, and in fact you might need to use asynchronous network operations to scale. And asynchronous code is hard. It can be really hard. Even a simple logic can be split in several callbacks and create endless issues. You can do amazing stuff with just a single thread, but you pay a price for it.

With virtual threads, you can write your actors using synchronous calls. Yep, boring, plain, synchronous calls, and your project will still scale like crazy. That's why Fibry was born: to let you write simple actors with synchronous logic.

Creating actors with the ActorSystem class

While using actors is very simple, there are several ways to create the actors and to use them, so you will need to decide how you want your system to be built.

The most flexible way to create actors is using ActorSystem, a class implementing a fluid interface. You might create anonymous and named actors, the difference being that named actors have a name and they can be used without having ac Actor object, and in fact you can send messages even before the actor has been created, which helps reducing race conditions. You can choose the strategy: AUTO (the default, using virtual threads if available), FIBER (using fibers, throwing an exception if they are not available) and THREAD (using threads). You can supply an initial state, which is mostly useful for thread confinement.

You can create several types of actor:

  • Normal Actors: they receive messages without returning any result; they need to implement Consumer or BiConsumer (if you need access to the actor)
  • Returning Actors: they compute a result and return a CompletableFuture for each message; they need to implement Function or BiFunction (if you need access to the actor)
  • Multi-messages actors: they can handle more than one type of message; they need a message handler with public methods in the form onXXX(message), and they can return or not a value
  • Receiving actors: they are a normal actor that can also "receive", meaning that they can ask the actor system to deliver some particular message while processing another message, e.g. if you are waiting for another actor to provide some information; they need to implement BiConsumer
  • Receiving and returning actors: they are receiving actors that can also return a result; they need to implement BiFunction

Please take into consideration that while Receiving actors are the most powerful, there is some overhead in their use, and the receive operation must be used carefully as in the worst case it might have to scan all the message in the queue. In fact, I expect many cases to be covered with returning actors (e.g. you ask something to another actor and wait for the result), and they should be preferred.

Let's see now how to create an actor:

var actor = ActorSystem.anonym

Related Skills

View on GitHub
GitHub Stars231
CategoryCustomer
Updated4d ago
Forks28

Languages

Java

Security Score

85/100

Audited on Mar 24, 2026

No findings