POJO-actor v1.0:轻量级 Actor 模型库(适用于 Java)

发布: (2025年12月23日 GMT+8 03:51)
11 min read
原文: Dev.to

I’m happy to translate the article for you, but I need the full text of the article (the content you’d like translated). Could you please paste the article’s body here? Once I have that, I’ll provide a Simplified Chinese translation while keeping the source link, formatting, markdown, and any code blocks exactly as they are.

概述

Actor 模型是一种编程范式,其中独立的实体(actor)仅通过消息传递进行通信,消除了锁和共享状态并发的复杂性。

在历史上,actor 框架依赖于操作系统线程,导致 actor 的数量受限于 CPU 核心数。随着 Java 21虚拟线程 的引入,即使是普通的笔记本电脑也可以同时容纳 数万 个 actor。

项目仓库:

架构

POJO‑actor 使用现代 Java 特性实现了简化的 actor 模型。仅约 800 行代码即可提供实用且高性能的解决方案。

ComponentDescription
ActorSystem管理 actor 生命周期并提供可配置的 work‑stealing 线程池
ActorRef指向 actor 的引用,提供 tell()(一次性发送)和 ask()(请求‑响应)API
Virtual Threads每个 actor 在其自己的虚拟线程上运行,以实现轻量级消息处理
Work‑Stealing Pools将繁重计算委派给可配置的线程池
Zero Reflection完全使用标准 JDK API 构建 → 支持 GraalVM Native Image

快速入门

Maven 依赖

<com.scivicslab>
    <artifactId>POJO-actor</artifactId>
    <version>1.0.0</version>
</com.scivicslab>

注意: 该库将在不久后发布到 Maven Central。期间请在本地安装:

git clone https://github.com/scivicslab/POJO-actor
cd POJO-actor
./mvnw install

基本用法

import com.scivicslab.pojoactor.ActorSystem;
import com.scivicslab.pojoactor.ActorRef;
import java.util.concurrent.CompletableFuture;

// Define a simple POJO
class Counter {
    private int count = 0;

    void increment() { count++; }
    int getValue()   { return count; }
}

// Create an actor system (4 threads for CPU‑intensive tasks)
ActorSystem system = new ActorSystem("mySystem", 4);
ActorRef counter = system.actorOf("counter", new Counter());

// Send messages
counter.tell(c -> c.increment());                     // fire‑and‑forget
CompletableFuture<Integer> result = counter.ask(c -> c.getValue()); // request‑response

int value = result.get(); // → 1

system.terminate();

Source:

任意 POJO 都可以成为 Actor

您不需要为 Actor 模型重新设计代码。任何现有的 Java 对象——甚至是标准库类——都可以瞬间成为 Actor。

import com.scivicslab.pojoactor.ActorSystem;
import com.scivicslab.pojoactor.ActorRef;
import java.util.ArrayList;
import java.util.concurrent.CompletableFuture;

ActorSystem system = new ActorSystem("listSystem");
ActorRef<ArrayList<String>> listActor = system.actorOf("myList", new ArrayList<>());

// Mutate the list
listActor.tell(list -> list.add("Hello"));
listActor.tell(list -> list.add("World"));
listActor.tell(list -> list.add("from"));
listActor.tell(list -> list.add("POJO-actor"));

// Query the list
CompletableFuture<Integer> sizeResult = listActor.ask(list -> list.size());
System.out.println("List size: " + sizeResult.get()); // → 4

CompletableFuture<String> first = listActor.ask(list -> list.get(0));
System.out.println("First element: " + first.get()); // → Hello

CompletableFuture<String> joined = listActor.ask(list -> String.join(" ", list));
System.out.println(joined.get()); // → Hello World from POJO-actor

system.terminate();

好处

  • 在不改变架构的情况下改造现有代码库
  • 为任何对象提供基于 Actor 的线程安全保护
  • 通过按需将对象转换为 Actor,实现增量式扩展
  • 复用现有 POJO,无需修改

大规模 Actor 可扩展性

Virtual threads 使 POJO‑actor 能够高效地处理成千上万的 actor。下面的示例创建了 10 000Counter actor:

import com.scivicslab.pojoactor.ActorSystem;
import com.scivicslab.pojoactor.ActorRef;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;

ActorSystem system = new ActorSystem("massiveSystem", 4); // 4 CPU 线程用于计算
List<ActorRef<Counter>> actors = new ArrayList<>();

// 创建 10 000 个 actor
for (int i = 0; i > futures = new ArrayList<>();
for (ActorRef<Counter> actor : actors) {
    futures.add(actor.tell(c -> c.increment()));
}

// 等待所有消息处理完毕
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).get();

// 验证每个计数器都已递增一次
for (ActorRef<Counter> actor : actors) {
    int value = actor.ask(c -> c.getValue()).get();
    assert value == 1;
}

System.out.println("Successfully processed messages for 10 000 actors!");
system.terminate();

此示例展示了

  • 在不耗尽线程的情况下创建成千上万的 actor
  • 依赖虚拟线程实现高效的消息处理
  • 与任意 POJO 的简洁、类型安全交互

尽情使用 POJO‑actor 构建高并发、轻量级的应用程序吧!

高级用法

并行矩阵乘法

POJO‑actor 在并行计算任务方面表现出色。下面的示例演示了使用工作窃取线程池进行大规模计算的分布式矩阵乘法:

// Create large matrices for multiplication
final int matrixSize = 400;
final int blockSize = 100;
double[][] matrixA = new double[matrixSize][matrixSize];
double[][] matrixB = new double[matrixSize][matrixSize];

// Create ActorSystem with 4 CPU threads for heavy computation
ActorSystem system = new ActorSystem("matrixSystem", 4);
List<CompletableFuture<double[][]>> futures = new ArrayList<>();

// Divide matrix into blocks and assign to different actors
for (int blockRow = 0; blockRow < matrixSize; blockRow += blockSize) {
    for (int blockCol = 0; blockCol < matrixSize; blockCol += blockSize) {
        Calculator calculator = new Calculator(); // hypothetical block calculator
        ActorRef<Calculator> actor = system.actorOf(
            String.format("block_%d_%d", blockRow, blockCol), calculator);

        // Light operation: Initialize actor with block coordinates (uses virtual thread)
        actor.tell(calc -> calc.initBlock(matrixA, matrixB, blockRow, blockCol)).get();

        // Heavy computation: Matrix multiplication (uses work‑stealing pool)
        CompletableFuture<double[][]> blockSum = actor.ask(
            calc -> calc.calculateBlock(),          // CPU‑intensive matrix multiplication
            system.getWorkStealingPool()           // Delegate to work‑stealing pool
        );

        futures.add(blockSum);
    }
}

// Wait for all parallel calculations to complete
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).get();

关键性能点

  • 轻量操作initBlock() 仅设置引用,因此虚拟线程非常适合。
  • 重计算calculateBlock() 执行实际的乘法运算;工作窃取线程池是必需的。
  • 虚拟线程 – 仅处理 setter/getter 级别的操作,几乎不占用 CPU。
  • CPU 控制 – 无论演员数量多少,只有 4 个 CPU 线程处理所有重工作。
  • 响应性 – 当重工作运行时,演员仍能对轻量消息保持响应。
  • 可扩展性 – 可以创建成千上万的演员而不会耗尽系统资源。

自定义线程池

ActorSystem system = new ActorSystem("system");

// Add additional work‑stealing pools
system.addWorkStealingPool(8);  // CPU‑intensive tasks
system.addWorkStealingPool(2);  // I/O‑bound tasks

// Use a specific thread pool
counter.tell(c -> c.increment(), system.getWorkStealingPool(1));

演员层级

ActorRef parent = system.actorOf("parent", new ParentActor());
ActorRef child  = parent.createChild("child", new ChildActor());

GraalVM 本地镜像支持

许多传统的演员框架依赖反射来进行消息路由、序列化和动态代理生成,这使得 native‑image 编译变得困难。POJO‑actor 不使用反射,仅使用现代 JDK 特性,因此可以在无需额外配置的情况下编译为 GraalVM 本地镜像。

# Compile to native image
native-image -jar target/POJO-actor-1.0.0-fat.jar -o pojo-actor-native

# Run native executable
./pojo-actor-native

不需要额外的配置文件或反射提示。

性能

  • 启动时间 – 通过本地编译实现近乎瞬时启动。
  • 内存使用 – 由于采用基于 POJO 的设计,堆分配极少。
  • 吞吐量 – 使用虚拟线程可实现高消息处理速率。
  • 可扩展性 – 采用高效的工作窃取线程池来并行任务。

性能最佳实践

了解何时使用虚拟线程与工作窃取线程池对于获得最佳性能至关重要。

轻量操作 — 使用默认虚拟线程

// 快速操作,不会阻塞或消耗大量 CPU
counter.tell(c -> c.increment());
counter.tell(c -> c.setName("newName"));
listActor.tell(list -> list.add("item"));

CompletableFuture<Integer> size = listActor.ask(list -> list.size());

重计算 — 委托给工作窃取线程池

ActorSystem system = new ActorSystem("system", 4); // 4 个 CPU 线程用于重工作

// CPU 密集型计算应使用工作窃取线程池
CompletableFuture<Result> result = calculator.ask(
    c -> c.performMatrixMultiplication(),
    system.getWorkStealingPool()
);

// I/O 密集或阻塞调用也应使用工作窃取线程池
CompletableFuture<Data> data = dataProcessor.ask(
    p -> p.readLargeFile(),
    system.getWorkStealingPool()
);

为什么这很重要

  • 虚拟线程 适用于轻量级的消息传递和状态变更。
  • 工作窃取线程池 处理 CPU 密集型任务,避免阻塞虚拟线程。
  • 这种划分可以防止重计算降低 actor 系统的响应性。

您可以通过配置工作窃取线程池的大小来控制 CPU 核心的使用:

// 示例:混合工作负载下正确使用线程池
ActorSystem system = new ActorSystem("mixedSystem", 4);
ActorRef processor = system.actorOf("processor", new DataProcessor());

示例用法

// Create an actor system
ActorSystem system = ActorSystem.create();

// Create a POJO actor
DataProcessor processor = system.actorOf(new DataProcessor());

// Light operation – uses a virtual thread
processor.tell(p -> p.updateCounter());

// Heavy operation – uses the work‑stealing pool
CompletableFuture<AnalysisResult> heavyResult = processor.ask(
    p -> p.performComplexAnalysis(largeDataset),
    system.getWorkStealingPool()
);

// The actor remains responsive to light messages while heavy computation runs in the background
processor.tell(p -> p.logStatus()); // This won’t be blocked by the heavy computation

要求

  • Java 21 或更高
  • Maven 3.6+

依赖

  • 运行时: 仅 JDK 标准库
  • 测试: JUnit 5,Apache Commons Math(仅测试范围)

构建

# Compile and test
mvn clean test

# Build JAR
mvn clean package

# Generate Javadoc
mvn javadoc:javadoc

致谢

POJO‑actor 受到了 Alexander Zakusylo 的 actr 库的启发,后者在 Java 中开创了基于 POJO 的 Actor 模型方法。虽然 actr 引入了许多优秀的概念,POJO‑actor 在此基础上进行了扩展和改进,具体包括:

  • 消息顺序保证: 与 actr 不同,POJO‑actor 确保发送给 Actor 的消息按照发送顺序进行处理。
  • 现代 Java 特性: 基于 Java 21+ 虚拟线程和现代并发模式构建。
  • 增强的线程池管理: actr 为每个 Actor 使用真实线程,导致可扩展性受限于 CPU 核心数,并在计算密集型任务下出现性能问题。POJO‑actor 为 Actor 使用虚拟线程,并将重计算任务委派给可配置的工作窃取线程池,从而在控制 CPU 核心使用的同时支持成千上万的 Actor。

我们还要感谢 Comedy.js——一个 Node.js Actor 框架,它启发了 POJO‑actor 的基本架构设计,尤其是 ActorSystemActorRef 概念。虽然 Comedy.js 为每个 Actor 使用单独的进程或真实线程,POJO‑actor 则利用 Java 的虚拟线程实现了成千上万的轻量级 Actor。

Back to Blog

相关文章

阅读更多 »