Eventloop
Overview
The Eventloop module is the basis for other modules that execute their code asynchronously within event loops and threads.
It provides efficient management of asynchronous operations without the overhead of multithreading. It is especially
useful for creating client-server applications with high-performance requirements. An Eventloop
class represents a Reactor
which is an interface that allows execution of tasks asynchronously or by a schedule.
Features
- Eventloop uses Java's NIO for asynchronous computation and I/O operations (TCP, UDP).
- Eliminates the traditional I/O bottleneck for further business logic processing.
- Can run multiple eventloop threads on available cores.
- Minimal GC load: arrays and byte buffers are reused.
- Eventloop can schedule/postpone certain tasks for deferred execution or background execution.
- Eventloop is single-threaded, so it has no concurrency overhead.
Reactor
Eventloop
is an implementation of the Reactor
interface. The Reactor
interface provides methods for executing tasks
asynchronously. It can also be used for scheduling tasks to be executed within Reactor
context (thread) later. There is also
an NioReactor
interface which extends the Reactive
interface and adds methods required for I/O operations (mostly network
operations).
Most of ActiveJ components depend not on Eventloop
class but on a Reactor
/NioReactor
interfaces.
There are also interfaces that mark components as reactive. Those are Reactive
and NioReactive
. Implementations of
these interfaces should implement a method getReactor()
which returns a Reactor
associated with a reactive component.
This approach allows for a convenient class hierarchy where asynchronous (e.g. reactive) components are separated from
synchronous components.
Eventloop is a low-level tool that you won't use directly in most cases. Nevertheless, it is an extremely important component that will give you an understanding of the ActiveJ asynchronous model.
Eventloop
is a
reactor that represents an infinite loop, where at each iteration
all the tasks that are stored in
special queues are executed. Those tasks can be either local, received from other threads, scheduled or
associated with I/O processing (via Selector
). Each of these tasks must be small, and its execution is called a tick
.
The only blocking operation of the Eventloop infinite loop is Selector.select()
. This operation selects a set of keys
whose corresponding channels are ready for I/O operations. Eventloop asynchronously processes the selected keys and executes
the queued runnables in a single thread.
Eventloop works with different types of tasks that are stored in separate queues:
Tasks | Added via | Description |
---|---|---|
Local tasks | Eventloop#post Eventloop#postLater Eventloop#postNext | Added from current reactor/eventloop thread |
Concurrent tasks | Eventloop#execute Eventloop#submit | Added from other threads |
Scheduled tasks | Eventloop#schedule Eventloop#delay | Scheduled to be executed later |
Background tasks | Eventloop#scheduleBackground Eventloop#delayBackground | Same as Scheduled, but if there are only Background tasks left, Eventloop will be stopped |
Eventloop will be stopped if its queues with non-background tasks are empty, the Selector has no selected
keys, and the number of concurrent operations in other threads is 0. To prevent Eventloop from closing, set the
keepAlive
flag. When it's set to true
, Eventloop will keep running even without tasks.
Examples
- BasicExample - a simple example of an eventloop that prints the "Hello World" message.
- EventloopExample - represents the sequence of operations in eventloops.
To run the examples, you need to clone ActiveJ from GitHub:
git clone https://github.com/activej/activej
And import it as a Maven project. Check out tag v6.0-beta2. Before running the examples, build the project. These examples are located at activej/examples/core/eventloop
.
Basic Example
In this example, we create an eventloop, post a task to it (which prints out the "Hello World" message) and then run the eventloop:
public final class BasicExample {
public static void main(String[] args) {
Eventloop eventloop = Eventloop.create();
eventloop.post(() -> System.out.println("Hello World"));
eventloop.run();
}
}
Eventloop Example
This example shows how tasks are scheduled in eventloops:
public final class EventloopExample {
public static void main(String[] args) {
Eventloop eventloop = Eventloop.builder()
.withCurrentThread()
.build();
long startTime = currentTimeMillis();
// #2
eventloop.delay(3000L, () -> System.out.println("Eventloop.delay(3000) is finished, time: " + (currentTimeMillis() - startTime)));
eventloop.delay(1000L, () -> System.out.println("Eventloop.delay(1000) is finished, time: " + (currentTimeMillis() - startTime)));
eventloop.delay(100L, () -> System.out.println("Eventloop.delay(100) is finished, time: " + (currentTimeMillis() - startTime)));
// #1
System.out.println("Before running eventloop, time: " + (currentTimeMillis() - startTime));
eventloop.run();
}
}
If you run the example, you get a result that looks something like this:
Before running eventloop, time: 2
Eventloop.delay(100) is finished, time: 106
Eventloop.delay(1000) is finished, time: 1001
Eventloop.delay(3000) is finished, time: 3001