我正在使用一个使用多个长时间运行的工作线程的流式java应用程序。应用程序接收数据,对其进行处理,然后使用SDK将其发送给第三方。有一个 Engine 类可以接收数据并将其提交给 Workers 。只要应用程序运行,工作线程就会活动,这可能是几个月甚至几年。
我已经包含了代表此问题的关键部分的示例代码。
import java.util.Map;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class BarEngine implements Engine
{
static Logger log = LoggerFactory.getLogger(BarEngine.class);
private static final int WORKER_COUNT = 5;
private BlockingQueue<Map<String, Object>> queue;
private FooWorker[] workers = new FooWorker[WORKER_COUNT];
public BarEngine()
{
for (int i = 0; i < WORKER_COUNT; i++)
{
workers[i] = new FooWorker(i, queue);
workers[i].start();
}
}
// From Engine Interface
@Override
public void sendEvent(final Map<String, Object> data)
{
try
{
queue.put(data);
}
catch (InterruptedException e)
{
log.error("Unexpected Exception", e);
}
}
// From Engine Interface
@Override
public void shutDown()
{
// Shuts down engine
}
public static class FooWorker extends Thread
{
static Logger log = LoggerFactory.getLogger(FooWorker.class);
private volatile boolean run = true;
private int id;
private BlockingQueue<Map<String, Object>> queue;
private Client client;
public FooWorker(int id, BlockingQueue<Map<String, Object>> queue)
{
this.id = id;
this.queue = queue;
client = Client.build(id);
}
@Override
public void run()
{
setName("FooWorker-" + id);
while (run)
{
try
{
Map<String, Object> data = queue.poll(5, TimeUnit.SECONDS);
if (null != data)
{
sendEvent(data);
}
}
catch (Throwable e)
{
log.error("Unexpected Exception", e);
}
}
}
private void sendEvent(Map<String, Object> data)
{
try
{
client.submit(data);
}
catch (Throwable e)
{
log.error("Unexpected Exception", e);
}
}
// dummy client classs
public static class Client
{
public void submit(Map<String, Object> data)
{
// submits data
}
public static Client build(int id)
{
// Builds client
return new Client();
}
}
}
}
我一直在做一些研究,但我没有找到满意的答案。
Executor
。不包括应用程序长寿命线程本身。Executor
管理长期存在的线程,但如果一个人应该使用Executor
来管理长实时线程,则不会回答我的问题是:我应该保持这些长时间运行Threads
裸吗?如果没有,我应该用什么替换它(如ExecutorService
或其他东西)?
答案 0 :(得分:0)
回答你的问题,如果你的线程与应用程序的生命周期相同,在我看来,如果你使用的是Thread或Executer服务(它再次使用下面的Threads)并不重要你正确地管理线程的生命周期。
从设计的角度来看,您的应用程序属于软件类别,我们称之为&#34;中间件&#34;。通常,中间件应用程序应该既高效又可扩展,这两者都是此类服务器的基本特性,但您忽略了这两者。您的应用程序的线程在每个线程上运行busy-wait循环,始终保持CPU忙。即使输入负载非常低,这种情况仍然存在。这种应用的质量不高。
或者,我建议您使用ThreadPool
实施,例如ThreadPoolExecutor
已经解决了您要在此处尝试完成的任务。如果此时所有最初启动的线程都忙,ThreadPoolExecutor
会利用BlockingQueue
的功能。如果负载较低,它也可以停止线程,如果需要,它可以再次启动。我编写了我提出的设计结构。看看下面的代码。我假设Client
不是线程安全的,所以我每个线程构造一个Client
。如果您的真实客户端实现是线程安全的,则可以在所有线程中使用一个客户端。
import java.util.Map;
import java.util.concurrent.*;
public class BarEngine implements Engine {
private static final int WORKER_COUNT = 5;
private ExecutorService threadPool;
public BarEngine() {
this.threadPool = new ThreadPoolExecutor(1, WORKER_COUNT, 10, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(100));
}
// From Engine Interface
@Override
public void sendEvent(final Map<String, Object> data) {
threadPool.submit(new FooWorker(data));
}
// From Engine Interface
@Override
public void shutDown() {
this.threadPool.shutdown();
// Shuts down engine
}
public static class FooWorker implements Runnable {
private final Client client;
private final Map<String, Object> data;
public FooWorker(Map<String, Object> data) {
client = Client.build(Thread.currentThread().getId());
this.data = data;
}
@Override
public void run() {
try {
if (null != data) {
sendEvent(data);
}
} catch (Throwable e) {
//todo log
}
}
private void sendEvent(Map<String, Object> data) {
try {
client.submit(data);
} catch (Throwable e) {
//todo log
}
}
// dummy client classs
public static class Client {
public void submit(Map<String, Object> data) {
// submits data
}
public static Client build(long id) {
// Builds client
return new Client();
}
}
}
}
答案 1 :(得分:0)
是的,您发布的内容正是ExecutorService的内容。一些建议:
通过执行程序创建ExecutorService,例如:
Executors.newFixedThreadPool(5);
如果您使用Executor服务,您可以通过Runnable将数据直接推送到ExecutorService,因为ExecutorService自行排队并将任务拆分为其工作人员......