Skip to main content

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.

note

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:

TasksAdded viaDescription
Local tasksEventloop#post Eventloop#postLater Eventloop#postNextAdded from current reactor/eventloop thread
Concurrent tasksEventloop#execute Eventloop#submitAdded from other threads
Scheduled tasksEventloop#schedule Eventloop#delayScheduled to be executed later
Background tasksEventloop#scheduleBackground Eventloop#delayBackgroundSame 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.
note

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();
}
}

See full example on GitHub

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

See full example on GitHub