← Назад к вопросам

В чем разница между многопоточностью и асинхронностью?

1.8 Middle🔥 121 комментариев
#Многопоточность

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Многопоточность vs Асинхронность: в чём разница?

Это одна из самых частых ошибок в понимании: многопоточность и асинхронность — это два разных механизма обработки конкурентных операций. Часто их путают, но они решают разные проблемы и имеют разные характеристики.

Основные понятия

Многопоточность (Multithreading):

  • Несколько потоков выполняются одновременно на одном или нескольких ядрах процессора
  • Переключение контекста между потоками управляется ОС
  • Каждый поток имеет свой stack, но делит heap
  • Требует синхронизации для безопасного доступа к общим данным

Асинхронность (Asynchrony):

  • Один поток может обрабатывать множество операций без блокировки
  • Когда операция ждёт (I/O, сеть), контроль возвращается вызывающему коду
  • Требует callback'ов или Future/Promise для получения результата
  • Event-driven модель

Визуальное сравнение

Многопоточность (Multithreading):

Время →

Поток 1: [Task A] [Task A] [Task A]
Поток 2:              [Task B] [Task B]
Поток 3:                          [Task C]

Все потоки выполняются параллельно (истинный параллелизм на многоядерных ЦП)

Асинхронность (Single-threaded async):

Время →

Поток 1: [Start A] → (ждёт I/O) ← продолжение A
         [Start B]    (ждёт I/O) ← продолжение B
         [Start C]    (ждёт I/O) ← продолжение C

Один поток, но операции не блокируют друг друга

Практические примеры на Java

1. Многопоточность: Thread-based

public class MultiThreadingExample {
  static class Task implements Runnable {
    private String name;
    
    public Task(String name) {
      this.name = name;
    }
    
    @Override
    public void run() {
      System.out.println(name + " started");
      try {
        Thread.sleep(2000); // Имитация I/O операции
      } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
      }
      System.out.println(name + " finished");
    }
  }
  
  public static void main(String[] args) {
    // Создаём несколько потоков
    Thread t1 = new Thread(new Task("Task 1"));
    Thread t2 = new Thread(new Task("Task 2"));
    Thread t3 = new Thread(new Task("Task 3"));
    
    // Все три потока выполняются параллельно
    t1.start();
    t2.start();
    t3.start();
    
    // Ждём завершения всех потоков
    try {
      t1.join();
      t2.join();
      t3.join();
    } catch (InterruptedException e) {
      Thread.currentThread().interrupt();
    }
    
    System.out.println("All tasks completed");
    // Выполнение: ~2 сек (все потоки работают параллельно)
  }
}

2. Асинхронность: Callback-based

public class AsyncCallbackExample {
  static class AsyncTask {
    public void execute(String taskName, Consumer<String> callback) {
      // Запускаем async операцию в отдельном потоке,
      // но основной поток не блокируется
      new Thread(() -> {
        try {
          System.out.println(taskName + " started");
          Thread.sleep(2000); // I/O операция
          callback.accept(taskName + " result");
        } catch (InterruptedException e) {
          Thread.currentThread().interrupt();
        }
      }).start();
    }
  }
  
  public static void main(String[] args) throws InterruptedException {
    AsyncTask task = new AsyncTask();
    
    // Запускаем три async задачи с callback'ами
    task.execute("Task 1", result -> System.out.println(result));
    task.execute("Task 2", result -> System.out.println(result));
    task.execute("Task 3", result -> System.out.println(result));
    
    System.out.println("Main thread continues");
    
    Thread.sleep(3000); // Ждём результатов
    System.out.println("Program finished");
    // Выполнение: ~2 сек (async операции не блокируют основной поток)
  }
}

3. Асинхронность: Future-based

public class AsyncFutureExample {
  public static void main(String[] args) throws Exception {
    ExecutorService executor = Executors.newFixedThreadPool(3);
    
    // Future — это объект, который будет содержать результат в будущем
    Future<String> future1 = executor.submit(() -> {
      System.out.println("Task 1 started");
      Thread.sleep(2000);
      return "Task 1 result";
    });
    
    Future<String> future2 = executor.submit(() -> {
      System.out.println("Task 2 started");
      Thread.sleep(2000);
      return "Task 2 result";
    });
    
    Future<String> future3 = executor.submit(() -> {
      System.out.println("Task 3 started");
      Thread.sleep(2000);
      return "Task 3 result";
    });
    
    // Основной поток может продолжать работу
    System.out.println("Main thread continues");
    
    // Получаем результаты когда они будут готовы
    System.out.println(future1.get());
    System.out.println(future2.get());
    System.out.println(future3.get());
    
    executor.shutdown();
    // Выполнение: ~2 сек
  }
}

4. Асинхронность: CompletableFuture (Java 8+)

public class CompletableFutureExample {
  public static void main(String[] args) throws Exception {
    // CompletableFuture — более мощная альтернатива Future
    CompletableFuture<String> task1 = CompletableFuture.supplyAsync(() -> {
      System.out.println("Task 1");
      try { Thread.sleep(2000); } catch (InterruptedException e) {}
      return "Result 1";
    });
    
    CompletableFuture<String> task2 = CompletableFuture.supplyAsync(() -> {
      System.out.println("Task 2");
      try { Thread.sleep(2000); } catch (InterruptedException e) {}
      return "Result 2";
    });
    
    // Composition: выполнить задачу после завершения другой
    CompletableFuture<String> combined = task1
      .thenCombine(task2, (r1, r2) -> r1 + " + " + r2)
      .thenApply(String::toUpperCase);
    
    System.out.println("Main continues");
    System.out.println(combined.get());
  }
}

5. Асинхронность: Reactive (Project Reactor)

public class ReactiveExample {
  public static void main(String[] args) {
    // Reactor — reactive library для асинхронного программирования
    Flux<String> flux = Flux
      .just("Task 1", "Task 2", "Task 3")
      .flatMap(task -> {
        return Mono.just(task)
          .delayElement(Duration.ofSeconds(1)) // I/O операция
          .map(t -> t + " result");
      });
    
    System.out.println("Main continues");
    
    flux.subscribe(result -> System.out.println(result));
    
    Thread.sleep(3000); // Ждём results
  }
}

Сравнительная таблица

ХарактеристикаМногопоточностьАсинхронность
ПотокиНесколько потоковОдин/Несколько потоков
ПараллелизмИстинный параллелизмКонкурентность, не параллелизм
Context switchingЧастый, дорогойРедкий, дешёвый
Memory overhead~2MB на потокМинимальный
СинхронизацияНужна (locks)Не нужна
Scalability1000-10000 потоков100000+ операций
СложностьСложная (deadlocks)Средняя (callback hell)
ИспользованиеCPU-bound задачиI/O-bound операции

Когда что использовать

Многопоточность лучше для:

  • CPU-intensive операции (вычисления, обработка данных)
  • Когда нужен истинный параллелизм на многоядерных ЦП
  • Простые операции с общим состоянием

Асинхронность лучше для:

  • I/O-bound операции (сеть, файлы, БД)
  • Обработка множества одновременных запросов
  • Когда нужна высокая пропускная способность
  • Микросервисная архитектура

Real-world пример: Web Server

Многопоточный подход (Thread per request):

public class ThreadPerRequestServer {
  public static void main(String[] args) throws IOException {
    ServerSocket serverSocket = new ServerSocket(8080);
    
    while (true) {
      Socket clientSocket = serverSocket.accept();
      // Каждый запрос — новый поток
      new Thread(() -> {
        // Обработка запроса
      }).start();
    }
  }
}
// Проблема: 1000 одновременных клиентов = 1000 потоков = OOM

Асинхронный подход:

@RestController
public class AsyncController {
  @GetMapping("/api/users/{id}")
  public CompletableFuture<UserDto> getUser(@PathVariable Long id) {
    return userService.findUserAsync(id)
      .thenApply(user -> new UserDto(user))
      .thenApply(dto -> { /* some processing */ return dto; });
  }
}
// Spring запускает обработку в фоне, освобождает поток для других запросов

Итоги

  1. Многопоточность — параллельное выполнение нескольких потоков
  2. Асинхронность — неблокирующее выполнение операций в одном потоке
  3. Они часто работают вместе (async операции могут выполняться в отдельных потоках)
  4. Выбор зависит от типа задачи: CPU-bound → многопоточность, I/O-bound → асинхронность
В чем разница между многопоточностью и асинхронностью? | PrepBro