AsyncTask использует шаблон пула потоков для запуска вещи из doInBackground ().Изначально проблема заключалась в том, что в ранних версиях ОС Android размер пула составлял всего 1, что означает отсутствие параллельных вычислений для группы AsyncTasks.Но позже они исправили это, и теперь размер равен 5, поэтому максимум 5 AsyncTasks могут работать одновременно.К сожалению, я не помню, в какой версии именно они это изменили.
ОБНОВЛЕНИЕ:
Вот что говорит текущий API (2012-01-27) об этом:
При первом представлении AsyncTasks выполнялись последовательно в одном фоновом потоке.Начиная с DONUT, это было изменено на пул потоков, позволяющий нескольким задачам работать параллельно.После HONEYCOMB планируется изменить это обратно на один поток, чтобы избежать распространенных ошибок приложения, вызванных параллельным выполнением.Если вы действительно хотите параллельное выполнение, вы можете использовать версию метода метода executeOnExecutor (Executor, Params ...) с THREAD_POOL_EXECUTOR;однако см. там комментарии для предупреждений о его использовании.
DONUT - это Android 1.6, HONEYCOMB - это Android 3.0.
ОБНОВЛЕНИЕ: 2
См. Комментарий kabuko
от Mar 7 at 1:27
.
Оказывается, что для API, где используется «пул потоков, позволяющий нескольким задачам работать параллельно» (начиная с версии 1.6 и заканчивая на версии 3.0)количество одновременно выполняемых AsyncTasks зависит от того, сколько задач уже передано для выполнения, но еще не завершило их doInBackground()
.
Это проверено / подтверждено мной на 2.2.Предположим, у вас есть пользовательский AsyncTask, который просто спит секунду в doInBackground()
.AsyncTasks использует очередь фиксированного размера для хранения отложенных задач.Размер очереди по умолчанию равен 10.Если вы запустите 15 своих пользовательских задач подряд, то первые 5 введут их doInBackground()
, а остальные будут ждать в очереди свободного рабочего потока.Как только любой из первых 5 завершается и, таким образом, освобождает рабочий поток, задача из очереди начнет выполнение.Таким образом, в этом случае не более 5 задач будут выполняться одновременно.Однако, если вы запустите 16 своих пользовательских задач подряд, то первые 5 введут их doInBackground()
, остальные 10 попадут в очередь, но 16-го будет создан новый рабочий поток, поэтому он немедленно начнет выполнение.Таким образом, в этом случае одновременно может выполняться не более 6 задач.
Существует ограничение на количество одновременно запускаемых задач.Поскольку AsyncTask
использует исполнителя пула потоков с ограниченным максимальным количеством рабочих потоков (128), а очередь отложенных задач имеет фиксированный размер 10, если вы попытаетесь выполнить более 138 своих пользовательских задач, приложение завершится с java.util.concurrent.RejectedExecutionException
.
Начиная с версии 3.0 API позволяет использовать ваш пользовательский пул потоков с помощью метода AsyncTask.executeOnExecutor(Executor exec, Params... params)
.Это позволяет, например, настроить размер очереди отложенных задач, если по умолчанию 10 не то, что вам нужно.
Как упоминает @Knossos, есть опция для использования AsyncTaskCompat.executeParallel(task, params);
из библиотеки поддержки v.4запускать задачи параллельно, не заботясь об уровне API.Этот метод устарел на уровне API 26.0.0.
ОБНОВЛЕНИЕ: 3
Вот простое тестовое приложение для игры с количеством задач, последовательное или параллельное выполнение: https://github.com/vitkhudenko/test_asynctask
ОБНОВЛЕНИЕ: 4 (спасибо @penkzhou за указание на это)
Начиная с Android 4.4 AsyncTask
ведет себя не так, как описано в ОБНОВЛЕНИЕ: 2 раздел. - это исправление , предотвращающее создание AsyncTask
слишком большого количества потоков.
До Android 4.4 (API 19) AsyncTask
имел следующие поля:
private static final int CORE_POOL_SIZE = 5;
private static final int MAXIMUM_POOL_SIZE = 128;
private static final BlockingQueue<Runnable> sPoolWorkQueue =
new LinkedBlockingQueue<Runnable>(10);
В Android 4.4 (API 19) вышеуказанные поля изменены следующим образом:
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
private static final BlockingQueue<Runnable> sPoolWorkQueue =
new LinkedBlockingQueue<Runnable>(128);
Это изменение увеличивает размер очереди до 128 элементов и уменьшает максимальное количество потоков до количества ядер ЦП *2 + 1. Приложения могут отправлять одинаковое количество задач.