如何使用固定数量的工作线程实现简单线程

时间:2020-03-06 14:38:29  来源:igfitidea点击:

我正在寻找实现以下目标的最简单,最直接的方法:

  • 主程序实例化工作线程来执行任务。
  • 一次只能运行n个任务。
  • 当达到n时,直到正在运行的线程数降到n以下为止,不再启动任何工作程序。

解决方案

使用执行器框架;即newFixedThreadPool(N)

我认为Executors.newFixedThreadPool符合要求。有多种不同的方法可以使用生成的ExecutorService,具体取决于我们是希望将结果返回到主线程,还是该任务完全独立,以及是否有要执行的任务集合,或者是否响应某些事件将任务排入队列。

Collection<YourTask> tasks = new ArrayList<YourTask>();
  YourTask yt1 = new YourTask();
  ...
  tasks.add(yt1);
  ...
  ExecutorService exec = Executors.newFixedThreadPool(5);
  List<Future<YourResultType>> results = exec.invokeAll(tasks);

另外,如果我们有一个新的异步任务要执行以响应某个事件,则可能只想使用ExecutorService的简单execute(Runnable)方法。

Executors.newFixedThreadPool(int)

Executor executor = Executors.newFixedThreadPool(n);

Runnable runnable = new Runnable() {
 public void run() {
  // do your thing here
 }
}

executor.execute(runnable);

如果要自己滚动:

private static final int MAX_WORKERS = n;
private List<Worker> workers = new ArrayList<Worker>(MAX_WORKERS);

private boolean roomLeft() {
    synchronized (workers) {
        return (workers.size() < MAX_WORKERS);
    }
}

private void addWorker() {
    synchronized (workers) {
        workers.add(new Worker(this));
    }
}

public void removeWorker(Worker worker) {
    synchronized (workers) {
        workers.remove(worker);
    }
}

public Example() {
    while (true) {
        if (roomLeft()) {
            addWorker();
        } 
    }
}

其中Worker是我们扩展Thread的类。完成工作后,每个工作人员都会调用此类的removeWorker方法,并将自身作为参数传递。

话虽如此,Executor框架看起来要好得多。

编辑:有人在乎解释为什么这是如此糟糕,而不是仅仅修改它吗?

正如这里的其他人所提到的,最好的选择是使用Executors类创建线程池:

但是,如果我们想自己动手,此代码应为我们提供一个进行下一步的思路。基本上,只需将每个新线程添加到线程组中,并确保该组中活动线程的数目永远不超过N:

Task[] tasks = getTasks(); // array of tasks to complete
ThreadGroup group = new ThreadGroup();
int i=0;
while( i<tasks.length || group.activeCount()>0 ) {
    if( group.activeCount()<N && i<tasks.length ) {
        new TaskThread(group, tasks[i]).start();
        i++;
    } else {
        Thread.sleep(100);
    }
}

/* Get an executor service that will run a maximum of 5 threads at a time: */
ExecutorService exec = Executors.newFixedThreadPool(5);
/* For all the 100 tasks to be done altogether... */
for (int i = 0; i < 100; i++) {
    /* ...execute the task to run concurrently as a runnable: */
    exec.execute(new Runnable() {
        public void run() {
            /* do the work to be done in its own thread */
            System.out.println("Running in: " + Thread.currentThread());
        }
    });
}
/* Tell the executor that after these 100 steps above, we will be done: */
exec.shutdown();
try {
    /* The tasks are now running concurrently. We wait until all work is done, 
     * with a timeout of 50 seconds: */
    boolean b = exec.awaitTermination(50, TimeUnit.SECONDS);
    /* If the execution timed out, false is returned: */
    System.out.println("All done: " + b);
} catch (InterruptedException e) { e.printStackTrace(); }