Java(Android)多线程进程

Har*_*shi 13 java multithreading android blockingqueue threadpoolexecutor

我正在开发应用程序(Matt的traceroute windows版本http://winmtr.net/),它创建多个线程,每个线程都有自己的进程(执行ping命令).ThreadPoolExecutor一段时间后关闭所有线程(例如10秒)

ThreadPoolExecutor 使用阻塞队列(在执行之前保存任务)

int NUMBER_OF_CORES = Runtime.getRuntime().availableProcessors();
ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(
    NUMBER_OF_CORES * 2, NUMBER_OF_CORES * 2 + 2, 10L, TimeUnit.SECONDS, 
    new LinkedBlockingQueue<Runnable>()
);
Run Code Online (Sandbox Code Playgroud)

PingThread.java

private class PingThread extends Thread {

    @Override
    public void run() {
        long pingStartedAt = System.currentTimeMillis();
        // PingRequest is custom object
        PingRequest request = buildPingRequest(params);

        if (!isCancelled() && !Thread.currentThread().isInterrupted()) {

            // PingResponse is custom object

            // Note:
            // executePingRequest uses PingRequest to create a command 
            // which than create a runtime process to execute ping command
            // using string response i am creating PingResponse

            PingResponse pingResponse = PingUtils.executePingRequest(request);

            if (pingResponse != null) {
                pingResponse.setHopLocation(hopLocation);                   
                // publish ping response to main GUI/handler
                publishProgress(pingResponse);
            } else
                Logger.error(
                    "PingThread", "PingResponse isNull for " + request.toString()
                );
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

现在,如果我创建多个线程在循环中说超过500并在池执行器内执行

执行线程

PingThread thread = new PingThread(params);
poolExecutor.execute(thread);
Run Code Online (Sandbox Code Playgroud)

我知道LinkedBlockingQueue在执行任务之前保留任务.每个线程的进程最多需要200到400毫秒,但通常小于10毫秒

我在做什么

for (int iteration = 1; iteration <= 50/*configurable*/; iteration++) {

    for (int index = 0; index < 10/*configurable*/; index++) {
        PingThread thread = new PingThread(someParams);
        poolExecutor.execute(thread);
    }

    try {
        Thread.sleep(500);
    } catch (InterruptedException e) {
        Logger.error(false, e);
    }   
}
Run Code Online (Sandbox Code Playgroud)

50次迭代将需要大约25秒,在这里我只有最多40个ping响应,其余被视为因超时而丢失.如果我增加迭代损失也会增加(由于没有线程增加而呈指数增长)

观察:

我在Galaxy S6上运行这个应用程序,它有8个内核,应用程序池大小为16,最大池大小为16 + 2,我知道处理器一次只运行一个线程,它共享一个量子时间进行并行处理.

通过ThreadPoolExecutor及时观察,我看到队列中有很多任务,超时后队列中仍然存在很多线程LinkedBlockingQueue

如果我减少没有线程它工作正常,但如果增加它会产生问题

问题:

  • 当我使用具有双核处理器的设备时,Ping响应会降低.
  • 为什么队列中存在多个线程,每个线程大约需要10到50ms(增加线程时间会增加300ms或更多)?
  • 它应该在给定的时间内完成,为什么不呢?
  • 如何克服这个问题?
  • 我应该使用,ConcurrentLinkedQueue但它使用生产者/消费者模型,不知何故ThreadPoolExecutor(我认为它)也使用这个模型.
  • LinkedBlockingQueue 在执行任务之前保存任务(线程空闲或在队列中),如何克服这个问题?
  • 通过设置Thread.MAX_PRIORITY后面的迭代不能解决问题(后来的迭代的线程在队列中)
  • 减少线程数没有解决问题的原因?因为队列中没有线程存在?
  • 有没有办法检查,如果队列中存在的线程招待他们然后执行其他线程,而不是在给定时间内阻止其他线程.
  • 添加5秒等额外时间不是解决方案
  • 更改corePoolSize如何让ThreadPoolExecutor的增加线程最大排队过吗?在我的情况下不起作用.

在测试期间,内存和处理器的使用受到限制.

详细答案/帮助是必需的.

编辑

当应用程序进入后台时,没有丢失,用户CPU使用率下降到0-2%,而焦点应用程序占用了4-6%的CPU使用率.是由于UI和其他ralted东西,我试图删除所有不必要的代码,我也改变PingThreadPingTask

PingTask implements Runnable {/*....*/}

注意: 我使用相同的代码创建了单独的基于java的应用程序,它在桌面上工作正常,所以我们可以说它是Android操作系统特定的问题吗?

bwt*_*bwt 5

我不确定这是否是导致所有问题的原因,但是你创造了许多不必要的线程.

你应该替换

private class PingThread extends Thread {
Run Code Online (Sandbox Code Playgroud)

用:

private class PingThread implements Runnable {
Run Code Online (Sandbox Code Playgroud)

或(使用更充分的名称):

private class PingTask implements Runnable {
Run Code Online (Sandbox Code Playgroud)

即提交给Executors 的任务不应该是自己的线程.它起作用,因为它是一种Thread工具Runnable,但你却在浪费它.


Har*_*shi 3

观察:

使用相同的代码创建并观察独立的java应用程序(日志)后,我了解到以下内容:

  • 不知何故,Android 操作系统架构和/或处理器限制了线程数量。
  • LinkedBlockingQueue在任务执行之前保存任务,因此如果队列很长,队列中后面的线程将不得不等待更多时间。
  • 增加/动态corePoolSize并且maxPoolSize正在做同样的事情,他们在队列中添加了线程
  • 应用程序使用 4-6% 的 CPU,因此我们不能说 CPU 超载或充分利用应用程序资源,但当应用程序进入后台时(GUI 或其他相关操作系统和/或基于应用程序的线程可能会停止或中断),CPU 使用率会降至 0 -3%。

解决方案:

对于 50 次迭代和 10 个内部创建 500 个线程,现在我做了两件事:

  • 增加Thread.sleep(millis)一些计算时间。
  • 减少每次迭代的线程数。我现在创建了 10 个线程 Math.ceil((double) 10 / 3) = 3,因此PingUtils.executePingRequest(pingRequest)每个线程有 3 个连续线程3 * 3 = 9,即保留 1 个线程,因此我们将为最后一个请求创建一个单独的线程。对于每次迭代,我现在创建 4 个线程,而不是创建 10 个线程。
  • 现在使用这种方法我有 200 个线程而不是 500 个,这解决了问题。