我当前正在实现一个具有addRecord(最终Redcord记录)和close()方法的数据容器/数据结构。
public class Container{
Container() {
}
public void addRecord(final Record record){
// do something with the record
// add the modified it to the container
}
public void close(){
}
}
由于要将记录存储到容器中需要完成几个步骤,因此我想将这些步骤外包给线程,然后再将它们最终添加到容器中。
我的问题是我不希望任何容器实例使用的空间超过我们说的4个线程,并且如果我同时打开多个容器实例的总数不超过,例如,应使用12个线程。除此之外,我还希望一旦创建了第4个容器,其他3个打开的容器中的每一个都应该丢失其线程之一,这样所有4个容器中的每一个都可以使用3个线程。
我正在研究ThreadPools和ExecutorServices。虽然设置大小为12的线程池很简单,但是在使用ExecutorServices时很难将使用的线程数限制为4。回想一下,我最多需要12个线程,这就是为什么我在容器实例之间共享一个大小为12的静态实例。
这就是我的开始
public class Container{
public static final ExecutorService SERVICE= Executors.newFixedThreadPool(12);
final List<Future<Entry>> _fRecords = new LinkedList<>();
Container() {
}
public void addRecord(final Record record){
_fRecords.add(SERVICE.submit(new Callable<Entry>{
@Override
public Entry call() throws Exception {
return record.createEntry();
}
}));
}
public void close() throws Exception {
for(final Future<Entry> f) {
f.get(); // and add the entry to the container
}
}
}
很显然,这不能确保单个实例最多使用4个线程。此外,如果另一个容器需要几个线程,我看不出如何强制这些Executor服务关闭工作程序或重新分配它们。
由于Redord本身是一个接口,并且createEntry()的运行时间取决于实际的实现,因此我想确保不同的容器不会使用相同的线程/工人,即,我不想任何线程都可以为两个不同的容器工作。其背后的想法是,我不希望仅处理“短时间运行记录”的容器会减慢仅处理“长时间运行”记录的容器实例的速度。如果从概念上讲这没有意义(对您的问题),则无需为不同的容器使用隔离的线程/工作者。
实现我自己的ExecutorService是否正确?有人可以指出我有类似问题/解决方案的项目吗?否则,我想我必须实现自己的ExecutorService并在我的容器中共享它,还是有更好的解决方案?
当前,我正在用close方法收集所有期货,因为容器必须按到达的顺序存储记录/条目。显然,这需要很多时间,因此,我希望在Future完成后再这样做。安排一个额外的工人单独处理期货是否有意义,还是让我的工人来完成该工作,即进行线程交互会更好?
答案 0 :(得分:0)
我认为Java运行时不能提供满足您需求的东西。
我编写了自己的课程来满足这样的需求(公共池+给定队列的限制)。
public static class ThreadLimitedQueue {
private final ExecutorService executorService;
private final int limit;
private final Object lock = new Object();
private final LinkedList<Runnable> pending = new LinkedList<>();
private int inProgress = 0;
public ThreadLimitedQueue(final ExecutorService executorService, final int limit) {
this.executorService = executorService;
this.limit = limit;
}
public void submit(Runnable runnable) {
final Runnable wrapped = () -> {
try {
runnable.run();
} finally {
onComplete();
}
};
synchronized (lock) {
if (inProgress < limit) {
inProgress++;
executorService.submit(wrapped);
} else {
pending.add(wrapped);
}
}
}
private void onComplete() {
synchronized (lock) {
final Runnable pending = this.pending.poll();
if (pending == null || inProgress > limit) {
inProgress--;
} else {
executorService.submit(pending);
}
}
}
}
在我的情况下,limit
的唯一区别是恒定的,但是您可以对其进行修改。例如,您可以将int limit
替换为Supplier<Integer> limitFunction
,并且此函数必须提供动态限制,例如
Supplier<Integer> limitFunction = 12 / containersList.size();
使其更加健壮(例如,如果containerList为空或超过12,该怎么办)