В чем разница между асинхронностью и параллелизмом?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
В чем разница между асинхронностью и параллелизмом
Эти два понятия часто путают, но они обозначают разные вещи. Давайте разберёмся с примерами.
Определения
Параллелизм (Parallelism) — это одновременное выполнение нескольких задач на разных ядрах процессора. Это физическое явление — задачи действительно выполняются в один и тот же момент времени.
Асинхронность (Asynchrony) — это концептуальный способ организации кода, при котором одна задача может начать другую задачу и продолжить работу, не дожидаясь её завершения.
Аналогия: ресторан
Параллелизм:
Повар 1 готовит блюдо A
Повар 2 готовит блюдо B
Повар 3 готовит блюдо C
Время: ████████████ (все готовят одновременно на разных плитах)
Асинхронность:
Один повар:
1. Начинает готовить блюдо A (поставить в духовку)
2. Не ждёт, начинает готовить блюдо B (пока A готовится)
3. Не ждёт, начинает готовить блюдо C
4. Блюдо A готово → берёт его
5. Продолжает готовить D
Время: ████ (один повар, но переключается между задачами)
В Java код
Параллелизм (использование многопоточности на мультиядерном CPU):
public class ParallelismExample {
public static void main(String[] args) throws InterruptedException {
long start = System.currentTimeMillis();
// Задача 1 в потоке 1
Thread thread1 = new Thread(() -> {
System.out.println("Поток 1 начал работу");
try { Thread.sleep(2000); } catch (InterruptedException e) {}
System.out.println("Поток 1 завершил работу");
});
// Задача 2 в потоке 2
Thread thread2 = new Thread(() -> {
System.out.println("Поток 2 начал работу");
try { Thread.sleep(2000); } catch (InterruptedException e) {}
System.out.println("Поток 2 завершил работу");
});
// На 2-ядерном CPU оба потока выполняются параллельно
thread1.start();
thread2.start();
thread1.join();
thread2.join();
long elapsed = System.currentTimeMillis() - start;
System.out.println("Время: " + elapsed + "ms");
// ~2000ms (параллельно), а не 4000ms (последовательно)
}
}
// Вывод:
// Поток 1 начал работу
// Поток 2 начал работу
// Поток 1 завершил работу
// Поток 2 завершил работу
// Время: ~2000ms
Асинхронность (CompletableFuture в одном потоке):
public class AsynchronousExample {
public static void main(String[] args) throws Exception {
long start = System.currentTimeMillis();
CompletableFuture<String> task1 = CompletableFuture.supplyAsync(() -> {
System.out.println("Задача 1 начала работу");
try { Thread.sleep(2000); } catch (InterruptedException e) {}
System.out.println("Задача 1 завершена");
return "Результат 1";
});
CompletableFuture<String> task2 = CompletableFuture.supplyAsync(() -> {
System.out.println("Задача 2 начала работу");
try { Thread.sleep(2000); } catch (InterruptedException e) {}
System.out.println("Задача 2 завершена");
return "Результат 2";
});
// Ждём завершения обеих задач
CompletableFuture.allOf(task1, task2).join();
long elapsed = System.currentTimeMillis() - start;
System.out.println("Время: " + elapsed + "ms");
// ~2000ms (асинхронно)
}
}
Основные различия
| Аспект | Параллелизм | Асинхронность |
|---|---|---|
| Ядра CPU | Разные ядра | Одно ядро (или пул) |
| Выполнение | Одновременно | Чередование |
| Блокировка | Может быть | Не блокирует |
| Сложность | Выше (синхронизация) | Ниже (callbаcks) |
| Масштабируемость | Ограничена ядрами | Большие числа |
| Применение | CPU-bound задачи | I/O-bound задачи |
Пример 1: Асинхронность на одном ядре (I/O операции)
public class IoAsyncExample {
public static void main(String[] args) throws Exception {
long start = System.currentTimeMillis();
// Асинхронная загрузка файлов
CompletableFuture<String> file1 = CompletableFuture.supplyAsync(() ->
readFile("file1.txt") // блокирующая операция
);
CompletableFuture<String> file2 = CompletableFuture.supplyAsync(() ->
readFile("file2.txt")
);
CompletableFuture<String> file3 = CompletableFuture.supplyAsync(() ->
readFile("file3.txt")
);
// Загружаются "асинхронно" (на самом деле в пуле потоков)
CompletableFuture.allOf(file1, file2, file3).join();
long elapsed = System.currentTimeMillis() - start;
System.out.println("Время: " + elapsed + "ms");
// ~time(file1) если параллельно на разных потоках
}
private static String readFile(String filename) {
// Блокирующая I/O операция
try { Thread.sleep(1000); } catch (InterruptedException e) {}
return "Content of " + filename;
}
}
Пример 2: Параллелизм (CPU-bound задачи)
public class ParallelProcessingExample {
public static void main(String[] args) throws Exception {
// CPU-bound вычисления на разных ядрах
long start = System.currentTimeMillis();
int[] data = new int[100000000];
// Последовательно (медленно)
long sum1 = 0;
for (int i = 0; i < data.length; i++) {
sum1 += data[i];
}
// Параллельно (быстро на мультиядерном CPU)
long sum2 = java.util.Arrays.stream(data)
.parallel() // разбить на части, обработать на разных ядрах
.sum();
System.out.println("Sequence: " + (System.currentTimeMillis() - start) + "ms");
System.out.println("Parallel: " + (System.currentTimeMillis() - start) + "ms");
}
}
// Parallel версия значительно быстрее на многоядерных CPU
Пример 3: Асинхронное программирование (CompletableFuture)
public class AsyncProgrammingExample {
public static void main(String[] args) throws Exception {
// Цепочка асинхронных операций
CompletableFuture.supplyAsync(() -> fetchUserData(1))
.thenApply(user -> fetchUserPosts(user.getId()))
.thenApply(posts -> filterPosts(posts))
.thenAccept(filtered -> System.out.println("Результат: " + filtered))
.join();
}
static class User { int id; }
static class Post { int id; }
private static User fetchUserData(int userId) {
System.out.println("Загружаю пользователя...");
try { Thread.sleep(1000); } catch (InterruptedException e) {}
return new User();
}
private static java.util.List<Post> fetchUserPosts(int userId) {
System.out.println("Загружаю посты...");
try { Thread.sleep(1000); } catch (InterruptedException e) {}
return new java.util.ArrayList<>();
}
private static java.util.List<Post> filterPosts(java.util.List<Post> posts) {
System.out.println("Фильтрую посты...");
return posts;
}
}
Пример 4: Реактивное программирование
import java.util.concurrent.CompletableFuture;
public class ReactiveExample {
public static void main(String[] args) throws Exception {
// Асинхронное слушание событий
CompletableFuture<String> event = new CompletableFuture<>();
// Слушатель (не блокирует)
event.thenAccept(data -> System.out.println("Получено: " + data));
// Отправитель (в другом потоке)
new Thread(() -> {
try { Thread.sleep(2000); } catch (InterruptedException e) {}
event.complete("Данные готовы!");
}).start();
System.out.println("Main не заблокирован");
// Продолжает работу, не ждя завершения
}
}
Когда использовать что
Параллелизм используй для:
- CPU-bound задачи (вычисления, обработка данных)
- Задачи требуют мощности CPU
- Пример: parallel streams, ForkJoinPool
Асинхронность используй для:
- I/O-bound операции (сеть, файлы, БД)
- Большое количество одновременных операций
- Отзывчивый UI (не блокировать главный поток)
- Пример: CompletableFuture, callbacks, reactive streams
Комбинация: Параллелизм + Асинхронность
public class HybridExample {
public static void main(String[] args) throws Exception {
// Асинхронно загружаем данные
CompletableFuture<int[]> dataFuture = CompletableFuture.supplyAsync(() ->
loadDataFromNetwork() // I/O операция
);
// Когда данные готовы, обрабатываем параллельно
CompletableFuture<Long> result = dataFuture.thenApply(data ->
java.util.Arrays.stream(data)
.parallel() // CPU-bound обработка
.sum()
);
Long sum = result.join();
System.out.println("Сумма: " + sum);
}
private static int[] loadDataFromNetwork() {
// I/O операция
try { Thread.sleep(2000); } catch (InterruptedException e) {}
return new int[1000000];
}
}
Вывод
Параллелизм:
- Физическое одновременное выполнение на разных ядрах
- Для CPU-bound задач
- Требует синхронизации
Асинхронность:
- Логическое чередование выполнения
- Для I/O-bound задач
- Позволяет одному потоку обрабатывать множество задач
В Java:
- Параллелизм:
Thread,ExecutorService,ForkJoinPool,.parallel() - Асинхронность:
CompletableFuture, callbacks, reactive streams (Project Reactor, RxJava)
Оба подхода могут использоваться вместе для максимальной эффективности.