Многопоточный процесс Java (Android)

Я работаю над приложением ( Mattes traceroute windows version 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>() ); 

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() ); } } } 

Теперь, если я создаю несколько потоков, скажем более 500 в цикле и выполняю внутри исполнятеля пула

Выполнение потоков

 PingThread thread = new PingThread(params); poolExecutor.execute(thread); 

Я знаю, что 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); } } 

50 итераций займут около 25 секунд, здесь у меня есть только до 40 пинг-ответов, считающихся потерями из-за тайм-аута. Если я увеличиваю итерации, также увеличивается потеря (экспоненциально из-за увеличения количества потоков)

Наблюдение:

Я запускаю это приложение на Galaxy S6 с 8 ядрами, размер пула приложений – 16, а максимальный размер пула – 16 + 2, я знаю, что процессор работает только по одному потоку за раз, он имеет квантовое время для параллельной обработки.

Наблюдая за ThreadPoolExecutor своевременно, я вижу много задач в очереди, после тайм-аута в очереди все еще много потоков из-за LinkedBlockingQueue

Если я уменьшаю нити потоков, он отлично работает, но если увеличение создает проблему

Проблема:

  • Ответы на пинг уменьшаются, когда я использую устройства с двухъядерным процессором.
  • Почему в очереди присутствует много потоков, где каждый поток занимает от 10 до 50 мс (увеличение времени потоков увеличится до 300 мс или более)?
  • Это должно завершиться в заданное время, почему его нет?
  • Как преодолеть эту проблему?
  • Должен ли я использовать ConcurrentLinkedQueue но он использует модель Producer / consumer, как-то ThreadPoolExecutor (я думаю, что это) тоже использует эту модель.
  • LinkedBlockingQueue выполняет задачи перед их выполнением (потоки простаивают или находятся в очереди), как это преодолеть?
  • Установив Thread.MAX_PRIORITY для последних итераций, эта проблема не решается (позже поток итерации находится в очереди)
  • Уменьшение количества потоков решает проблему, почему? Потому что в очереди меньше нитей в очереди?
  • Есть ли способ проверить, если потоки, присутствующие в очереди, развлекают их, тогда выполняйте другие, не блокируя другие потоки, но в течение заданного времени.
  • Добавление дополнительного времени, такого как 5 секунд, не является решением
  • Изменение corePoolSize как в том, как заставить ThreadPoolExecutor увеличить потоки до max перед очередью? Не работает в моем случае.

Во время тестирования использование памяти и процессора ограничено.

Требуется подробный ответ / справка.

редактировать

Когда приложение переходит в фоновый режим, потери и загрузка процессора пользователя снижаются до 0-2%, в то время как на приложение фокуса заняло 4-6% использования процессора. Это связано с UI и другими раллийными вещами, я попытался удалить все ненужные коды и я изменил PingThread на PingTask

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

Примечание. Я создал отдельное приложение на основе Java, используя тот же код, и он отлично работает на рабочем столе, поэтому можем ли мы сказать, что это проблема конкретной ОС Android?

Solutions Collecting From Web of "Многопоточный процесс Java (Android)"

Я не уверен, что это вызывает все проблемы, но вы создаете много ненужных потоков.

Вы заменяете

 private class PingThread extends Thread { 

с :

 private class PingThread implements Runnable { 

Или (используя более подходящее имя):

 private class PingTask implements Runnable { 

Т.е. задачи, предъявленные Executor , не должны быть самими потоками. Он работает, потому что Thread реализует Runnable , но вы тратите его впустую.

Наблюдение:

После создания и наблюдения независимого java-приложения (журналов) с использованием того же кода я узнал следующее:

  • Так или иначе, архитектура и / или процессорная архитектура Android ограничивают количество потоков.
  • LinkedBlockingQueue выполняет задачи перед их выполнением, поэтому, если у нас будет длинная очередь, потоки в очереди будут ждать больше.
  • Увеличение / Динамический corePoolSize и maxPoolSize делают то же самое, они добавили потоки в очередь
  • Приложение использует 4-6% CPU, поэтому мы не можем сказать, что процессор перегружает или использует полные ресурсы приложения, но когда приложение переходит в фоновый режим (графический интерфейс или другие связанные с ним ОС и / или потоки, основанные на приложениях, могут останавливаться или прерываться). CPU использует капли до 0 -3%.

Решение:

Для 50 итераций и 10 внутренних создает 500 потоков теперь я сделал две вещи:

  • Увеличьте время Thread.sleep(millis) с помощью некоторых вычислений.
  • Уменьшите количество потоков для каждой итерации. Я создал 10 потоков теперь Math.ceil((double) 10 / 3) = 3 поэтому у нас есть 3 последовательных PingUtils.executePingRequest(pingRequest) для каждого потока, т. PingUtils.executePingRequest(pingRequest) 3 * 3 = 9 остается 1, поэтому мы создадим отдельный поток для последнего запрос. Для каждой итерации вместо создания 10 потоков теперь я создаю 4 потока.
  • Используя этот подход, теперь у меня есть 200 потоков вместо 500, которые решают проблему.

Темы создают новый уникальный объект, а runnable позволяет всем потокам совместно использовать один объект. Таким образом, вы не должны распространять Thread при попытке многопоточности, вместо этого используйте Runnable:

 class RunnableDemo implements Runnable { private Thread thread; String threadName="My thread"; public void run() { //do your code from here 'logic' System.out.println("Threading is Running"); try { for(int i = 4; i > 0; i--) { System.out.println("Thread: "+threadName +" "+ i); // Let the thread sleep for a while. Thread.sleep(50); //sleep your content for xx miliseconds } } catch (InterruptedException e) { System.out.println("Thread " + threadName + " interrupted."); } System.out.println("Thread " + threadName + " exiting."); //finish your work here } public void start () { System.out.println("Starting " + threadName ); if (thread == null) { thread = new Thread (this); thread.start (); //This will call your run methods of Runnable } } } //test your thread from here public class TestThread { public static void main(String args[]) { RunnableDemo R1 = new RunnableDemo( "Thread-1"); R1.start(); RunnableDemo R2 = new RunnableDemo( "Thread-2"); R2.start(); } }