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

时间:2008-09-24 03:52:45

标签: java multithreading concurrency

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

  • 主程序实例化worker 执行任务的线程。
  • 一次只能运行n个任务。
  • 达到n时,不再有工人 直到计数开始 正在运行的线程回落到n以下。

7 个答案:

答案 0 :(得分:53)

我认为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)方法。

答案 1 :(得分:23)

/* 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(); }

答案 2 :(得分:6)

Executors.newFixedThreadPool(int)

Executor executor = Executors.newFixedThreadPool(n);

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

executor.execute(runnable);

答案 3 :(得分:2)

使用Executor框架;即newFixedThreadPool(N)

答案 4 :(得分:1)

  1. 如果您的任务队列不受限制且任务可以在较短的时间间隔内完成,您可以使用Executors.newFixedThreadPool(n);正如专家建议的那样。

    此解决方案的唯一缺点是无界任务队列大小。你无法控制它。任务队列中的巨大堆积会降低应用程序的性能,并可能在某些情况下导致内存不足。

  2. 如果您要使用ExecutorService并启用 work stealing 机制,其中空闲工作线程共享繁忙工作线程的工作负载通过窃取任务队列中的任务。它将返回ForkJoinPool类型的Executor Service。

      

    public static ExecutorService newWorkStealingPool(int parallelism)

         

    创建一个线程池,该线程池维护足够的线程以支持给定的并行度级别,并且可以使用多个队列来减少争用。并行度级别对应于主动参与或可用于任务处理的最大线程数。实际线程数可能会动态增长和缩小。工作窃取池不保证提交任务的执行顺序。

  3. 由于API的灵活性,我更喜欢ThreadPoolExecutor来控制许多参数,这些参数控制着流任务的执行。

    ThreadPoolExecutor(int corePoolSize, 
                           int maximumPoolSize, 
                           long keepAliveTime, 
                           TimeUnit unit, 
                           BlockingQueue<Runnable> workQueue, 
                           ThreadFactory threadFactory,
                           RejectedExecutionHandler handler)
    
  4. 在您的情况下,同时设置corePoolSize and maximumPoolSize as N。在这里,您可以控制任务队列大小,定义自己的自定义线程工厂和拒绝处理程序策略。

    查看相关的SE问题以动态控制池大小:

    Dynamic Thread Pool

答案 5 :(得分:0)

如果你想自己动手:

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框架看起来好多了。

编辑:任何人都想解释为什么这么糟糕,而不仅仅是降低它?

答案 6 :(得分:0)

正如其他人提到的那样,你最好的办法就是建立一个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);
    }
}