Многопоточность это такая абстракция поверх многоядерности и многосокетности. В ней нет деталей аппаратуры, таких как NUMA/APIC, несимметричные ядра и т.д., но и того, что есть - достаточно, чтобы закопаться.
Исторически первым подходом был, вроде бы, OpenMPI - стандарт на директивы компилятора для распараллеливания в фортране, Си и Си++. Туда даже лезть страшно и несмотря на то, что в Gentoo Linux есть USE=“mpi”, я туда никогда не лазил. И не хочу, вот вообще.
Дальше есть библиотеки для работы с многопоточностью и примитивами синхронизации, libpthread, в ней хорошо то, что она в стандарте POSIX, и писал бы на Си/Си++, наверное бы изучил. Но кто сейчас на Си писать будет? Сейчас будут писать на Haskell (если повыпендриваться) или Java (чтобы не мучаться). Этот последний способ и хотелось бы изучить.
Что рекомендуют сейчас?
- Thread
- Parallel Streams
- ExecutorService
- ForkJoinPool
- CompletableFuture
А как же ты до этого-то жил, если никакую многопоточность не умеешь? Да как-то и так всего хватало (первого способа - создать одну нить), процессоры были малокорные, максимум чтобы пользовательский интерфейс не лочился вторую нить запустить. А сейчас вот захотелось прям, чтобы ядер было много, и чтобы все работали.
ExecutorService
реализует интерфейс для пула потоков, очередь задач и управление ресурсами. Благодаря ему можно ограничить сверху количество запускаемых потоков.
int cores = Runtime.getRuntime().availableProcessors();
ExecutorService executorService = Executors.newFixedThreadPool(cores - 1); // одна нить это UI
Смотреть надо реализацию интерфейса ExecutorService
, то есть класс ThreadPoolExecutor
.
Какие есть ресурсы для изучения многопоточности?
public class MyTask implements Runnable {
public void run() {
System.out.println("Executing the run() method...");
}
}
...
MyTask task = new MyTask(some_init_parameters);
threadPoolExecutor.execute(task);
Пока всё отлично, если эти нити не лезут к одним и тем же ресурсам (например если надо поиск по файлам сделать, то задачи нарезает одна нить, а все остальные нити работают каждая со своим файлом и никак не пересекаются).
Интересное начинается, когда все нити должны работать над одной монолитной объектной моделью в памяти (грубо говоря, с общим графом). Вот такой туториал покажите, пожалуйста.