Многопоточность вообще и в джаве

Многопоточность это такая абстракция поверх многоядерности и многосокетности. В ней нет деталей аппаратуры, таких как NUMA/APIC, несимметричные ядра и т.д., но и того, что есть - достаточно, чтобы закопаться.

Исторически первым подходом был, вроде бы, OpenMPI - стандарт на директивы компилятора для распараллеливания в фортране, Си и Си++. Туда даже лезть страшно и несмотря на то, что в Gentoo Linux есть USE=“mpi”, я туда никогда не лазил. И не хочу, вот вообще.

Дальше есть библиотеки для работы с многопоточностью и примитивами синхронизации, libpthread, в ней хорошо то, что она в стандарте POSIX, и писал бы на Си/Си++, наверное бы изучил. Но кто сейчас на Си писать будет? Сейчас будут писать на Haskell (если повыпендриваться) или Java (чтобы не мучаться). Этот последний способ и хотелось бы изучить.

Что рекомендуют сейчас?

  1. Thread
  2. Parallel Streams
  3. ExecutorService
  4. ForkJoinPool
  5. 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);

Пока всё отлично, если эти нити не лезут к одним и тем же ресурсам (например если надо поиск по файлам сделать, то задачи нарезает одна нить, а все остальные нити работают каждая со своим файлом и никак не пересекаются).

Интересное начинается, когда все нити должны работать над одной монолитной объектной моделью в памяти (грубо говоря, с общим графом). Вот такой туториал покажите, пожалуйста.