基于Callable的某些属性选取ThreadPool线程

时间:2015-09-01 09:40:53

标签: java multithreading

我有一个Callable任务,我想提交给执行官:

public static final class PersonalTask implements Callable<Object> {

  private final String name;
  private final int sleep;

  public PersonalTask(String name, int sleep) {
    this.name = name;
    this.sleep = sleep;
  }

  @Override
  public Object call() throws Exception {
    System.out.format("My name is %s and I'm sleeping for %d seconds%n", name, sleep);
    Thread.sleep(sleep * 1000);
    return null;
  }

}

每个任务都包含请求执行任务的人员的名称以及一段时间的休眠。这个睡眠持续时间是真实用例的代理,它会调用一些昂贵的操作。

为了促进这些任务,我使用了 5个线程的固定线程池

private static final ExecutorService executor = Executors.newFixedThreadPool(5);

为了说明我的问题,我想将以下内容提交给线程池:

public static void main(String[] args) throws Exception {
  List<PersonalTask> tasks = Arrays.asList(new PersonalTask[] { 
      new PersonalTask("Bob", 10), new PersonalTask("Bob", 10), 
      new PersonalTask("Bob", 10), new PersonalTask("Bob", 10),
      new PersonalTask("Bob", 10), new PersonalTask("Bob", 10),
      new PersonalTask("Eric", 1), new PersonalTask("Janice", 2) });

  executor.invokeAll(tasks);
}

这个输出是:

My name is Bob and I'm sleeping for 10 seconds
My name is Bob and I'm sleeping for 10 seconds
My name is Bob and I'm sleeping for 10 seconds
My name is Bob and I'm sleeping for 10 seconds
My name is Bob and I'm sleeping for 10 seconds

*** PAUSE FOR 10 SECONDS ***

My name is Bob and I'm sleeping for 10 seconds
My name is Eric and I'm sleeping for 1 seconds
My name is Janice and I'm sleeping for 2 seconds

这是因为Bob睡眠10秒的任务使5个可用线程饱和,剩下的任务 - 特别是属于Eric和Janice的任务 - 必须等待那些完成。

这是不公平的! Bob提交的大量/慢速工作正在使可用线程饱和并且饿死 Eric和Jane。

我想为ExecutorService提供一种机制来区分要求安排的任务,因此我可以提出 fairer 解决方案。

我现在想保持这个非常简单。 Bob的所有任务都应该由池中的同一个线程处理。为了简单起见,我想使用PersonalTask.name.hashCode() % threadPoolSize并使用它来选择要使用的线程。

这意味着Bob只能使用其中一个可用的5个线程。这将使剩余的4个线程可以自由处理其他人的请求。

我意识到这并不完美,因为具有相同hash % size价值的其他人仍然会被排在Bob之后。实际上,他们现在必须等待很长时间,,因为在该线程的队列中有6 * 10 second个工作。

我可以在Java中使用哪些模式来实现此目的?

2 个答案:

答案 0 :(得分:0)

使用您自己的BlockingQueue实施(如PriorityBlockingQueue)和手动构建的ThreadPoolExecutor,将BlockingQueue作为参数接受。在BlockingQueue实现中,您可以保留一组所有现有人员(在任务中)并迭代它并返回该人员的下一个可用任务。

答案 1 :(得分:0)

在您的执行程序服务中,使用Map<Object,Queue>将每个用户映射到他们的任务,并将此用户与其在线程池中的线程关联,可能在另一个地图Map<Object,Thread>中。