是否有一个非线程替代 运行 对象并发或 运行 永不结束循环而不阻塞主线程?
Is there a non-Thread alternative to run objects concurrently or to run never ending loop without blocking the main thread?
我的目标是同时 运行 多个对象,而不会由于可伸缩性问题而创建新线程。其中一种用法是 运行建立一个保持活动的套接字连接。
while (true) {
final Socket socket = serverSocket.accept();
final Thread thread = new Thread(new SessionHandler(socket)).start();
// this will become a problem when there are 1000 threads.
// I am looking for alternative to mimic the `start()` of Thread without creating new Thread for each SessionHandler object.
}
为简洁起见,我将使用 Printer anology。
我尝试过的:
- 使用CompletableFuture,检查后,使用
ForkJoinPool
是一个线程池
我认为可行的方法:
- 演员模型。老实说,这个概念今天对我来说是新的,我仍在弄清楚如何 运行 对象方法而不阻塞主线程。
main/java/SlowPrinter.java
public class SlowPrinter {
private static final Logger logger = LoggerFactory.getLogger(SlowPrinter.class);
void print(String message) {
try {
Thread.sleep(100);
} catch (InterruptedException ignored) {
}
logger.debug(message);
}
}
main/java/NeverEndingPrinter.java
public class NeverEndingPrinter implements Runnable {
private final SlowPrinter printer;
public NeverEndingPrinter(SlowPrinter printer) {
this.printer = printer;
}
@Override
public void run() {
while (true) {
printer.print(Thread.currentThread().getName());
}
}
}
test/java/NeverEndingPrinterTest.java
@Test
void withThread() {
SlowPrinter slowPrinter = new SlowPrinter();
NeverEndingPrinter neverEndingPrinter = new NeverEndingPrinter(slowPrinter);
Thread thread1 = new Thread(neverEndingPrinter);
Thread thread2 = new Thread(neverEndingPrinter);
thread1.start();
thread2.start();
try {
Thread.sleep(1000);
} catch (InterruptedException ignored) {
}
}
目前,创建新线程是我所知道的唯一解决方案。但是,当有 1000 个线程时,这就成了问题。
您正在寻找 ScheduledExecutorService
。
创建一个初始 ScheduledExecutorService
具有固定适当数量的线程,例如Executors.newScheduledThreadPool(5)
5 个线程,然后您可以安排一个重复性任务,例如service.scheduleAtFixedRate(task, initialDelay, delayPeriod, timeUnit)
.
当然这样内部会用到线程,但是不会有你关心的上千线程的问题
过去很多开发者想出的解决方案就是ThreadPool。它通过重用相同的有限线程集避免了创建许多线程的开销。
然而,它要求您将工作分成小部分,并且您必须 link 一步一步地执行工作流程,否则您将在单独的单个方法中执行这些工作线。这就是 CompletableFuture 的结果。
Actor 模型是一种更奇特的建模技术,用于在流程中分配单独的步骤,但它们将再次在有限数量的线程上执行,通常每个 actor 仅 1 或 2 个。
有关以这种方式解决的问题的非常好的理论解释,请参阅 https://en.wikipedia.org/wiki/Staged_event-driven_architecture
如果我回顾一下您原来的问题,您的问题是您希望从多个来源接收保持活动消息,并且不想为每个来源使用单独的线程。
如果您使用像 while (socket.getInputStream().read() != -1) {}
这样的阻塞 IO,您将始终需要每个连接一个线程,因为该实现会在等待数据时让线程休眠,因此线程在此期间不能做任何其他事情。
相反,您真的应该研究一下 NIO。您只需要 1 个选择器和 1 个线程,您可以在其中连续检查选择器是否有来自任何来源的传入消息(不阻塞线程),并使用类似 HashMap 的东西来跟踪哪个来源仍在发送消息。
另见
NIO API 非常底层,顺便说一句,所以使用像 Netty 这样的框架可能更容易上手。
我的目标是同时 运行 多个对象,而不会由于可伸缩性问题而创建新线程。其中一种用法是 运行建立一个保持活动的套接字连接。
while (true) {
final Socket socket = serverSocket.accept();
final Thread thread = new Thread(new SessionHandler(socket)).start();
// this will become a problem when there are 1000 threads.
// I am looking for alternative to mimic the `start()` of Thread without creating new Thread for each SessionHandler object.
}
为简洁起见,我将使用 Printer anology。
我尝试过的:
- 使用CompletableFuture,检查后,使用
ForkJoinPool
是一个线程池
我认为可行的方法:
- 演员模型。老实说,这个概念今天对我来说是新的,我仍在弄清楚如何 运行 对象方法而不阻塞主线程。
main/java/SlowPrinter.java
public class SlowPrinter {
private static final Logger logger = LoggerFactory.getLogger(SlowPrinter.class);
void print(String message) {
try {
Thread.sleep(100);
} catch (InterruptedException ignored) {
}
logger.debug(message);
}
}
main/java/NeverEndingPrinter.java
public class NeverEndingPrinter implements Runnable {
private final SlowPrinter printer;
public NeverEndingPrinter(SlowPrinter printer) {
this.printer = printer;
}
@Override
public void run() {
while (true) {
printer.print(Thread.currentThread().getName());
}
}
}
test/java/NeverEndingPrinterTest.java
@Test
void withThread() {
SlowPrinter slowPrinter = new SlowPrinter();
NeverEndingPrinter neverEndingPrinter = new NeverEndingPrinter(slowPrinter);
Thread thread1 = new Thread(neverEndingPrinter);
Thread thread2 = new Thread(neverEndingPrinter);
thread1.start();
thread2.start();
try {
Thread.sleep(1000);
} catch (InterruptedException ignored) {
}
}
目前,创建新线程是我所知道的唯一解决方案。但是,当有 1000 个线程时,这就成了问题。
您正在寻找 ScheduledExecutorService
。
创建一个初始 ScheduledExecutorService
具有固定适当数量的线程,例如Executors.newScheduledThreadPool(5)
5 个线程,然后您可以安排一个重复性任务,例如service.scheduleAtFixedRate(task, initialDelay, delayPeriod, timeUnit)
.
当然这样内部会用到线程,但是不会有你关心的上千线程的问题
过去很多开发者想出的解决方案就是ThreadPool。它通过重用相同的有限线程集避免了创建许多线程的开销。
然而,它要求您将工作分成小部分,并且您必须 link 一步一步地执行工作流程,否则您将在单独的单个方法中执行这些工作线。这就是 CompletableFuture 的结果。
Actor 模型是一种更奇特的建模技术,用于在流程中分配单独的步骤,但它们将再次在有限数量的线程上执行,通常每个 actor 仅 1 或 2 个。
有关以这种方式解决的问题的非常好的理论解释,请参阅 https://en.wikipedia.org/wiki/Staged_event-driven_architecture
如果我回顾一下您原来的问题,您的问题是您希望从多个来源接收保持活动消息,并且不想为每个来源使用单独的线程。
如果您使用像 while (socket.getInputStream().read() != -1) {}
这样的阻塞 IO,您将始终需要每个连接一个线程,因为该实现会在等待数据时让线程休眠,因此线程在此期间不能做任何其他事情。
相反,您真的应该研究一下 NIO。您只需要 1 个选择器和 1 个线程,您可以在其中连续检查选择器是否有来自任何来源的传入消息(不阻塞线程),并使用类似 HashMap 的东西来跟踪哪个来源仍在发送消息。
另见
NIO API 非常底层,顺便说一句,所以使用像 Netty 这样的框架可能更容易上手。