在服务中实现线程池

时间:2014-01-22 08:29:20

标签: java android multithreading android-service

我正在努力实现一个Service,当被请求时,它将在几个并行线程上执行一些工作。

我的实施基于ThreadPoolExecutor课程以及LinkedBlockingQueue

作为一项基本规则,一旦完成所有任务并且队列中没有待处理的任务,我想停止服务(虽然稍后可以再次启动服务并遵循相同的逻辑)。

我已经能够使用下面的代码达到预期的结果,但我不确定这种方法是否正确。

public class TestService extends Service {
    // Sets the initial threadpool size to 3
    private static final int CORE_POOL_SIZE = 3;

    // Sets the maximum threadpool size to 3
    private static final int MAXIMUM_POOL_SIZE = 3;

    // Sets the amount of time an idle thread will wait for a task before terminating
    private static final int KEEP_ALIVE_TIME = 1;

    // Sets the Time Unit to seconds
    private static final TimeUnit KEEP_ALIVE_TIME_UNIT = TimeUnit.SECONDS;

    // A queue of Runnables for the uploading pool
    private final LinkedBlockingQueue<Runnable> uploadQueue = new LinkedBlockingQueue<Runnable>();

    // A managed pool of background upload threads
    private final ThreadPoolExecutor uploadThreadPool = new ThreadPoolExecutor(
            CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_TIME, KEEP_ALIVE_TIME_UNIT,
            uploadQueue) {

        @Override
        protected void afterExecute(Runnable r, Throwable t) {
            super.afterExecute(r, t);

            if (getActiveCount() == 1 && getQueue().size() == 0) {
                // we're the last Runnable around + queue is empty, service can be
                // safely stopped.
                TestService.this.stopSelf();
            }
        }
    };

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // execute a new Runnable
        uploadThreadPool.execute(new TestRunnable());

        /**
         * Indicating that if Android has to kill off this service (i.e. low memory),
         * it should not restart it once conditions improve.
         */
        return START_NOT_STICKY;
    }

    @Override
    public void onDestroy() {
        uploadThreadPool.shutdownNow();
        uploadQueue.clear();

        super.onDestroy();
    }
}

所以我有一些我还不确定的事情。

  1. 假设调用了onDestroy,是否可以安全地假设我的实现将中断所有正在运行的线程并安全地清除待处理的任务而不会以某种方式中断ThreadPoolExecutor类的实现?我问的原因是因为队列与执行程序相关联,并且shutdownNow可能是异步的,具体取决于队列的状态。有没有更好的方法呢?

  2. 我是否正确在onDestroy内实施此逻辑?根据我的经验,有些情况会导致服务被杀死(即内存不足),并且不会调用此回调。我是否应该在其他地方采用类似的方法?

  3. 宣布我的队列是否更好?执行者类成员为静态? - 如@TheTwo "Excecutor cannot be re-used once shutdown is called"所述。

  4. ThreadPoolExecutor课程需要BlockingQueue,使用其他类型的BlockingQueue实施(即ArrayBlockingQueue)的优缺点是什么?

  5. 关于我当前检测到队列为空的方式&amp;没有更多的待处理任务(特别是在afterExecute回调内) - 这是最好的方法吗?或者我可以得到队列为空并且任务以其他方式完成的指示吗?

  6. 感谢任何帮助!

3 个答案:

答案 0 :(得分:5)

我认为您正在尝试实施一项服务,该服务引入了许多问题,但却没有解决任何问题。实际上,您可以将调用代码减少一行 - 执行程序的创建 - 但是会删除对其进行精细控制的能力。调度许多任务没有任何好处,因为这已经由OS的线程调度程序解决了。此外,恶意调用者可以通过添加足够的while(true) sleep(100);循环来破坏其他几个程序。

关于你的问题:

  1. 您无法确保所有线程都被正确中断,因为无法中断未正确观察中断标志的线程。除while(true) ;之外,System.exit()不能被中断。理论上你可以停止一个线程,但是这个特性由于某种原因而被弃用,因为它可以使实际任务处于不完整/未完成状态(即将TCP连接保持半开)。

  2. 不,你没有正确实现它。对于一旦剩下的任务,队列就会在虚空中消失,然后一旦调用shutdown就不能重新使用Excecutor。所以你至少需要在服务启动时创建一个新的Excecutor实例,你真的应该弄清楚如何处理其余的任务。

  3. 不,因为2。

  4. 列表类型的优缺点取决于您的用例。 ArrayList在增长/收缩时成本较高,但在索引特定元素(indexOf)时成本较低,而链表则相反。由于你的队列总是添加到尾部,不关心任何其他元素而是第一个,并且它经常增长/缩小,链接列表是最佳选择。

  5. 您根本不应该以这种方式停止任务,因为未定义线程的执行顺序。在最坏的情况下,您的呼叫程序每次都会中断,直到服务执行完毕,这将导致服务不断启动&amp;停止没有特别的原因,同时浪费了大量的处理时间。为什么你甚至想要停止服务?如果它无关,除了使用几个字节的内存之外什么都不做。

答案 1 :(得分:4)

几年前,我写了this article来管理服务中的线程。您可以自己使用该软件,也可以自己了解如何自行完成。

答案 2 :(得分:2)

  1. 否。 shutdownNow尝试中断当前正在执行的任务。无法保证它能够做到这一点
  2. 是的。从文档中:

      

    一旦这些情况都不成立,服务的onDestroy()   调用方法并有效终止服务。所有   应该完成清理(停止线程,取消注册接收器)   从onDestroy()返回时。

  3.   
  4. 没有理由将这些成员声明为静态。 static成员与类关联,而不是与任何对象关联,公共用于在不同(在您的案例中为服务)实例之间共享相同的静态成员。
  5.   
  6. 您应该仔细阅读有关BlockingQueue<E>界面的所有可能实现的文档,但我怀疑,对于正常情况下使用,您会看到与性能视角的差异。
  7.