【问题标题】:java thread reusage via executor通过执行器重用java线程
【发布时间】:2011-04-14 07:23:45
【问题描述】:

我对以下内容感到困惑:
要在Java程序中使用线程,最简单的方法是扩展Thread类并实现runnable接口(或简单地实现runnable)。
开始线程的执行。我们必须调用线程的方法start(),然后调用线程的方法run()。于是线程开始了。
start() 方法(除非我错了)必须为每个线程准确且仅调用一次。因此,线程实例不能被重用,除非 run 方法本身以某种方式运行在一些有助于自定义实现线程重用的无限循环中。
现在的javadoc link text

调用执行将重用以前构造的线程(如果可用)

我不明白这是如何实现的。 我在 executor 方法的 execute 方法中提供了我的自定义线程,例如

  ExecutorService myCachedPool = Executors.newCachedThreadPool();
  myCachedPool.execute(new Runnable(){public void run(){  
     //do something time consuming

  }});

我委托给执行器框架的这个自定义线程如何被重用?
是否允许 Executor 调用方法 start() 超过 1 次,而我们不能在我们的程序中? 我是不是误会了什么?

谢谢。

【问题讨论】:

    标签: java multithreading threadpool executorservice executors


    【解决方案1】:

    要多次“启动”一个线程,请创建一个可运行的。例如:

    //NO
    private class T extends Thread { //not necessary to implement runnable
        public void run(){
            //...
        }
    }
    void someMethod(){
        T a = new T();
        a.start();
        a.start(); //NO NO NO NO NO NO NO NO NO NO NO NO NO NO NO NO NO
    }
    

    相反,

    //Yes
    private class T implements Runnable {
        public void run(){
            //...
        }
    }
    void someMethod(){
        T a = new T();
        new Thread(a).start();
        new Thread(a).start(); //YES YES YES
    }
    

    也可以这样做:

    void someMethod(){
        final Runnable r = new Runnable(){
            public void run(){
                //...
            }
        };
        new Thread(r).start();
        new Thread(r).start();
    }
    // r could also be a field of you class. 
    

    【讨论】:

      【解决方案2】:

      请注意,调用start() 的不是Executor,而是ExecutorService。不,它不是两次调用start()。它不会启动您直接使用Thread.start()... 给它的任务,而是启动一个知道该线程池工作队列的线程。线程基本上会等到有一些工作要做,然后拿起它并执行它,然后再回到等待状态。所以虽然线程执行了几个任务,Thread.start() 只被调用一次。

      编辑:从 cmets 来看,您对 Runnable(要执行的任务)和 Thread(要执行的任务)之间的区别有点困惑。

      同一个线程可以执行多个任务。对于不使用线程池的非常简单的示例,请考虑:

      public class MultiRunnable implements Runnable
      {
          private final List<Runnable> runnables;
      
          public MultiRunnable(List<Runnable> runnables)
          {
              this.runnables = runnables;
          }
      
          public void run()
          {
              for (Runnable runnable : runnables)
              {
                   runnable.run();
              }
          }
      }
      

      (忽略在多个线程中使用List&lt;T&gt; 的潜在线程安全问题。)

      您可以创建一大堆能够做不同事情的Runnable 任务,然后创建一个MultiRunnable 依次运行它们。将MultiRunnable 的实例传递给Thread 构造函数,然后当您启动线程时,它将执行每个原始可运行任务。这有帮助吗?

      【讨论】:

      • @Jon:对不起,我失去了你。所以框架的内部线程得到重用,而不是我作为参数传递的可运行对象来执行?我委托给框架的线程每次都会被重新实例化,但是由框架创建的内部线程的同一个实例?
      • @user384706:是的。您的可运行文件 不是 线程 - 它只是要执行的任务。您需要区分两者;它们非常不同。
      • @Jon:非常感谢。我唯一不确定的是,使用 Executors.newCachedThreadPool(); 有什么好处?因为如果我实现可运行(对于任务)的类的实例化成本很高,它将不会被重用,并且框架的同一线程将继续使用任务的新实例。那么我将从这个 api 中获得什么?除非概念是每个实现 runnable 的类都是最小的。
      • @user384706 创建线程的开销相对较大,对此您无能为力。相比之下,创建一个类的实例非常便宜(如果你让它变得昂贵,例如它在其构造函数中访问数据库/读取文件等,你可以重构它,不像线程的开销你无法控制)
      • @nos:所以我的任务不应该是扩展 Thread 而只是实现可运行?因为如果do extend Thread,那么线程创建的“比较大的开销”还是存在的。对吗?
      【解决方案3】:

      它不会多次调用 start();相反,池中的线程永远不会完成,而只是保持活动状态---等待。源代码可以下载,如果你想看的话。

      线程池中的每个Thread可以简单的wait()让Executor交给它一个新的Runnable,但是Thread自己的run()方法还没有完成。它只是等待将新的 Runnable 提供给 Executor。

      【讨论】:

      • 所以你的意思是在javadoc中它说“以前构造的线程将被重用”它不是指我传递给执行的runnable,而是执行器框架的内部线程?那么我的 Runnable 会被线程池的同一个线程重新实例化吗?我明白你在说什么了吗?
      • 您的 Runnable 不会被重新实例化。您的 Runnable 实例将被分配给池中的线程之一。该线程有自己的 run() 方法,它不能(不能)被 Runnable 的 run() 方法替换。线程的 run() 方法将调用您的Runnablerun() 方法,然后在您的Runnable.run() 完成后,线程最终将(必须完成一些簿记)回到waiting .
      猜你喜欢
      • 2014-08-27
      • 2017-12-29
      • 2018-04-12
      • 2019-02-03
      • 2019-12-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-05-18
      相关资源
      最近更新 更多