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 2012 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. Приложения могут по-прежнему отправлять такое же количество задач.