代码之家  ›  专栏  ›  技术社区  ›  shsteimer

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

  •  49
  • shsteimer  · 技术社区  · 17 年前

    我正在寻找实现以下功能的最简单、最直接的方法:

    • 主程序实例化worker 执行任务的线程。
    • 只有 n 任务可以同时运行。
    • 什么时候? n 已经联系到,没有更多的工人 开始到计数 正在运行的线程将落回下面 n .
    7 回复  |  直到 9 年前
        1
  •  53
  •   Ravindra babu    9 年前

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

        2
  •  22
  •   Fabian Steeg    17 年前
    /* 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(); }
    
        3
  •  5
  •   Ravindra babu    9 年前

    Executors.newFixedThreadPool(int)

    Executor executor = Executors.newFixedThreadPool(n);
    
    Runnable runnable = new Runnable() {
     public void run() {
      // do your thing here
     }
    }
    
    executor.execute(runnable);
    
        4
  •  2
  •   Ravindra babu    9 年前

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

        5
  •  1
  •   Community Mohan Dere    9 年前
    1. 如果任务队列不受限制,任务可以在较短的时间间隔内完成,则可以使用 Executors.newFixedThreadPool(n) ;专家建议。

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

    2. 如果你想用 ExecutorService 使能 work stealing 空闲工作线程通过在任务队列中窃取任务来共享繁忙工作线程的工作负载的机制。它将返回ForkJoinPool类型的执行器服务。

      公共静态 ExecutorService newWorkStealingPool (int并行)

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

    3. 我更喜欢 ThreadPoolExecutor 由于API能够灵活地控制许多paratemeter,这些paratemeter控制流任务的执行。

      ThreadPoolExecutor(int corePoolSize, 
                             int maximumPoolSize, 
                             long keepAliveTime, 
                             TimeUnit unit, 
                             BlockingQueue<Runnable> workQueue, 
                             ThreadFactory threadFactory,
                             RejectedExecutionHandler handler)
      

    在您的情况下,设置两者 corePoolSize and maximumPoolSize as N . 在这里,您可以控制任务队列大小,定义自己的自定义线程工厂和拒绝处理程序策略。

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

    Dynamic Thread Pool

        6
  •  0
  •   David Basarab    17 年前

    如果你想自己滚:

    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是扩展线程的类。每个工作者都将调用这个类的removeworker方法,并在完成任务后将自己作为参数传入。

    这么说来,执行器框架看起来好多了。

    编辑:有人想解释一下为什么这件事这么糟糕,而不是简单地修改一下?

        7
  •  0
  •   Ravindra babu    9 年前

    正如这里其他人提到的,您最好的选择是使用 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);
        }
    }