我正在设置一个服务器(Radius)的模拟器(用于测试),它使用线程将查询发送到另一个服务器(LDAP)。 查询需要以每秒x为单位执行。 我正在使用一个可调用的调度线程池执行程序,以便我可以创建callables并将它们提交给线程池执行。 每个线程都应该打开自己的连接并使用它来进行查询。 问题是我希望每次使用连接都被同一个线程重用。
澄清:
如果我允许说20个线程池我想要创建和使用20个连接。 (所以我可以发送10.000个查询,这些查询将由20个线程/连接依次处理)。
现在要连接的(LDAP)服务器信息被发送到callable的构造函数,并且callable设置连接以便执行。此后,我使用未来的可调用系统检索结果。 这个问题是每次我创建一个可调用的连接被打开(当然后来关闭)。
我正在寻找保持连接存活的最佳实践,并为每个线程重用它们。
我已经想到了实现这个目标的一些方法,但它们看起来效率不高:
实现此目的的最有效方法是什么?< - 旧问题,请参阅编辑新问题。
修改 我在想,因为我不能安全地得到一个线程号,但是threadId总是唯一的,我可以使用
map<String/threadId, connection>
将整个地图(参考)传递给callable。这样我就可以使用:(伪代码)
Connection con = map.get(this.getThreadId());
If (con == null){
con = new Connection(...);
map.put(this.getThreadId(), con)
}
也可以使地图静态,只是静态访问它。这样我就不必将地图传递给Callable。 这至少是安全的,并没有迫使我重构我的代码。
新问题: 与最佳实践更接近的内容;以上解决方案或Zim-Zam的解决方案? 如果以上情况最好,那么选择静态解决方案会更好吗?
答案 0 :(得分:1)
我会使用在BlockingQueue
之间共享的Callables
来实现这一点,ScheduledThreadPoolExecutor
每x
次BlockingQueue
次{/ 1}}次
public class Worker implements Runnable {
private final BlockingQueue<Query> inbox;
private final BlockingQueue<Result> outbox;
public Worker(BlockingQueue<Query> inbox, BlockingQueue<Result> outbox) {
// create LDAP connection
this.inbox = inbox;
this.outbox = outbox;
}
public void run() {
try {
while(true) {
// waits for a Query to be available
Query query = inbox.take();
// execute query
outbox.add(new Result(/* result */));
}
} catch(InterruptedException e) {
// log and restart? close LDAP connection and return?
}
}
}
public class Master {
private final int x; // number of queries per second
private final BlockingQueue<Query> outbox = new ArrayBlockingQueue<>(4 * x);
private final BlockingQueue<Result> inbox = new ArrayBlockingQueue<>(4 * x);
private final ScheduledThreadPoolExecutor executor;
private final List<Future<?>> workers = new ArrayList<>(20);
private final Future<?> receiver;
public Master() {
// initialize executor
for(int i = 0; i < 20; i++) {
Worker worker = new Worker(inbox, outbox);
workers.add(executor.submit(worker));
}
receiver = executor.submit(new Runnable() {
public void run() {
while(!Thread.interrupted()) {
try {
Result result = inbox.take();
// process result
} catch(InterruptedException e) {
return;
}
}
}
}
}
executor.scheduleWithFixedDelay(new Runnable() {
public void run() {
// add x queries to the queue
}
}, 0, 1, TimeUnit.SECONDS);
}
使用BlockingQueue#add
向Queries
添加新outbox
,如果这会引发异常,那么您的队列已满,您需要降低查询创建率和/或创造更多的工人。要在其cancel(true)
上突破某个工作人员的无限循环调用Future
,这会在InterruptedException
内部抛出Worker
。