【问题标题】:How to make a thread limit in Java如何在Java中进行线程限制
【发布时间】:2011-12-28 03:07:38
【问题描述】:

假设我有 1000 个文件要读取,并且由于某些限制,我想并行读取最多 5 个文件。而且,一旦其中一个完成,我想要一个新的开始。

我有一个拥有文件列表的主函数,我尝试在一个线程完成时更改计数器。但它不起作用!

有什么建议吗?

以下是主函数循环

for (final File filename : folder.listFiles()) {

    Object lock1 = new Object();
    new myThread(filename, lock1).start();
    counter++;
    while (counter > 5);
}

【问题讨论】:

    标签: java multithreading limit


    【解决方案1】:

    生成这样的线程不是要走的路。使用ExecutorService 并将池指定为5。将所有文件放在BlockingQueue 或另一个线程安全集合中,所有正在执行的文件都可以随意poll()

    public class ThreadReader {
    
        public static void main(String[] args) {
            File f = null;//folder
            final BlockingQueue<File> queue = new ArrayBlockingQueue<File>(1000);
            for(File kid : f.listFiles()){
                queue.add(kid);
            }
    
            ExecutorService pool = Executors.newFixedThreadPool(5);
    
            for(int i = 1; i <= 5; i++){
                Runnable r = new Runnable(){
                    public void run() {
                        File workFile = null;
                        while((workFile = queue.poll()) != null){
                            //work on the file.
                        }
                    }
                };
                pool.execute(r);
            }
        }
    }
    

    【讨论】:

    • +1。没有意义重新发明轮子。不过,我认为您的意思是 ThreadPoolExecutor。据我所知,J2SE 中没有 ExecutorPool。
    • Jdk5 以后有很多用于线程处理的 inbuit 类。正如 kylar 建议的那样,最好使用 Executorpool
    • 是的,我实际上是指 ExecutorService。修复并添加了超级粗略的源代码,为您提供要点。
    • 这与使用 Runnable 创建 5 个线程并没有什么不同。
    • 这太过分了。您不需要队列和 ExecutorService。因为 ExecutorService 是一个固定的线程池,你可以将所有的任务提交给它,它们会一次运行五个,直到它们全部完成。无需排队。
    【解决方案2】:

    您可以将 ExecutorService 用作线程池和队列。

    ExecutorService pool = Executors.newFixedThreadPool(5);
    File f = new File(args[0]);
    
    for (final File kid : f.listFiles()) {
        pool.execute(new Runnable() {
            @Override
            public void run() {
                process(kid);
            }
        });
    }
    pool.shutdown();
    // wait for them to finish for up to one minute.
    pool.awaitTermination(1, TimeUnit.MINUTES);
    

    【讨论】:

      【解决方案3】:

      Kylar 答案中的方法是正确的。使用 Java 类库提供的执行器类,而不是自己从头开始实现线程池(很糟糕)。


      但我认为讨论您问题中的代码以及为什么它不起作用可能会很有用。 (我已经尽可能地填写了一些你遗漏的部分......)

      public class MyThread extends Thread {
      
          private static int counter;
      
          public MyThread(String fileName, Object lock) {
              // Save parameters in instance variables
          }
      
          public void run() {
              // Do stuff with instance variables
              counter--;
          }
      
          public static void main(String[] args) {
              // ...
              for (final File filename : folder.listFiles()) {
                  Object lock1 = new Object();
                  new MyThread(filename, lock1).start();
                  counter++;
                  while (counter > 5);
              }
              // ...
          }
      }
      

      好的,那么这有什么问题呢?为什么它不起作用?

      第一个问题是,在main 中,您正在读写counter,而没有进行任何同步。我假设它也被工作线程更新 - 否则代码没有意义。所以这意味着主线程很有可能看不到子线程所做的更新结果。换句话说,while (counter &gt; 5); 可能是一个无限循环。 (事实上​​,这很有可能。JIT 编译器可以生成代码,其中counter &gt; 5 只是测试在前面的counter++; 语句之后留在寄存器中的counter 的值。

      第二个问题是您的while (counter &gt; 5); 循环非常浪费资源。您是在告诉 JVM 轮询一个变量……它每秒可能会执行数十亿次……完全运行一个处理器(内核)。你不应该那样做。如果你打算使用低级原语来实现这种东西,你应该使用 Java 的 Object.wait()Object.notify() 方法;例如主线程等待,每个工作线程通知。

      【讨论】:

        【解决方案4】:

        无论您使用什么方法来创建一个新线程,增加一个全局计数器,在线程创建周围添加一个条件语句,如果已达到限制,则不要创建新线程,可能会将文件推送到队列中(列表?)然后您可以添加另一个条件语句,在创建线程后,如果队列中有项目,则首先处理这些项目。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-02-17
          • 2019-03-15
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-07-09
          • 1970-01-01
          • 2019-01-04
          相关资源
          最近更新 更多