Java. Багатопотоковість

Процеси і потоки

Коли ми запускаємо програму, запускається процес, який «відображає» виконання програми. Тобто містить всю необхідну інформацію/дані для роботи цієї програми.

Кожен процес має власний адресний простір і послідовність процесорних команд для виконання.

Розділення цих частин (процесів) виправдано тим, що в одному процесі може існувати кілька виконавчих послідовностей команд, які спільно використовують ті самі дані.

Набір таких команд називається потоком. Він використовує спільний адресний простір і виконується процесором. Оскільки в системі може бути багато потоків одночасно, завдання ОС полягає в організації процесів, перемиканні між ними і планування їх виконання.

До елементів процесу відносять:

  • захищений адресний простір:

#include <stdio.h>

int main()

{

    int array[] = {};

    array[999999999999] = 1;

}

Результатом виконання буде: segfault.
Іншими словами, ми спробували перезаписати ділянку пам’яті, яка належить іншому процесу. ОС гарантує безпечну міжпроцесну взаємодію, тому виявляє це і не дозволяє таких операцій.

  • дані, які діляться всім процесом (ці дані можуть використовуватися всіма його потоками)

  • інформація про використання ресурсів (відкриті файли, мережеві з'єднання і т.д.)

  • інформація про потоки процесу

Елементи потоку включають:

  • стан процесора (набір даних з його регістрів), зокрема, лічильник поточної інструкції процесора (вказівник на поточну інструкцію, що виконується)

  • стек потоку (область пам'яті, де знаходяться локальні змінні потоку та адреси повернення функцій, які були викликані в його коді)

Тепер ми можемо визначити сам термін “Багатопотоковість”

Багатопотоковість - це одночасне (з точки зору програміста) виконання деяких дій різними фрагментами коду

Важливе зауваження: процесор може виконувати інструкції лише одного потоку в конкретний момент часу, тому є планувальник, який розподіляє час процесора між потоками для забезпечення їх “одночасного” виконання.

На основі алгоритмів такого планування визначають, який з потоків виконуватиметься в конкретний момент/коли потрібно припинити виконання потоку, щоб переключитися на інший потік.

Може виникнути питання

Якщо в конкретний момент процесор може виконувати інструкції тільки одного потоку, то як можна виконувати деякі операції одночасно?

— Якщо завантажити одне ядро декількома послідовностями завдань, воно по черзі перемикатиметься між ними (кожному потоку, як зазначалося, виділяється певний час виконання), обробляючи одне завдання з кожного потоку.

Якщо у вас є процесор з одним ядром, ви не матимете реального одночасного виконання. Процесор просто перемикається між потоками, це настільки швидко, що здається, що програми на вашому комп'ютері працюють одночасно.

Однак, варто зауважити, що залежно від реалізації багатопотоковості, можуть виникнути проблеми зі синхронізацією даних між потоками та конкуренцією за ресурси. Але це вже окрема тема.

Давайте коротко пробіжимось по вищезгаданому тексту

Процес - це програма. У кожного процесу є головний виконавчий потік. Він містить інструкції для виконання процесором - ваш код. Але іноді одного потоку недостатньо, тому можемо створити ще один, тобто інший фрагмент, частину коду, яка буде виконуватись.

Приклад

Ось невеликий JavaFx додаток

фейковий UserDao:

public Optional<User> findUserByUsername(String username) {
    waitSomeTime(3); // симуляція довгого пошуку в базі даних
    return users.stream()
                .filter(user -> user.getUsername().equals(username))
                .findFirst();
}

Контролер JavaFx:
При натисканні кнопки буде викликано цей метод.
Всі методи обробника виконуються на спеціальному потоці JavaFX (Single-threaded Rendering).

@FXML
void findInfo(ActionEvent event) {
Optional<User> userByUsername = userDao.findUserByUsername(input.getText());
userByUsername.ifPresentOrElse(user -> info.setText(user.toString()),
        () -> info.setText("No user found with this username"));
}

У цьому обробнику ми викликаємо метод, який спить протягом 3 секунд, імітуючи довгий пошук у базі даних.

Оскільки цей метод буде виконаний JavaFx потоком, замість того, щоб рендерити GUI, ми заблокуємо наш потік, виконуючи пошук у базі даних.
Годинник у правому верхньому куті зупиняється під час виконання методу(gif link).

Все, що нам потрібно зробити, це помістити "блокувальний код" в інший потік:

@FXML
void findInfo(ActionEvent event) {
    new Thread(() -> {
      Optional<User> userByUsername = userDao.findUserByUsername(input.getText());
userByUsername.ifPresentOrElse(user -> info.setText(user.toString()),
        () -> info.setText("No user found with this username"));
    }).start();
}

Замість явного створення потоку (“new Thread(…)”), ми маємо використовувати API представлений у Java 1.5 — ExecutorService, але тут наглядно можемо спостерігати створення нового потоку.

Результат:

Це вся багатопотоковість (ні 🙂)
Один потік обробляє графічний інтерфейс користувача, а інший отримує доступ до бази даних, блокується (очікує), виводить результат.

Поділись своїми ідеями в новій публікації.
Ми чекаємо саме на твій довгочит!
Oleksandr Klymenko
Oleksandr Klymenko@overpathz

Java Software Engineer

4.4KПрочитань
1Автори
71Читачі
На Друкарні з 19 квітня

Більше від автора

  • Secure networking. Deep Dive

    Глибоке занурення в протоколи TLS/SSL та інфраструктуру відкритих ключів (PKI). Основні поняття, процес встановлення захищеного з'єднання, роль сертифікатів та ланцюжка довіри

    Теми цього довгочиту:

    Security
  • Поширені помилки у дизайні REST API

    У довгочиті розглядаються поширені помилки при проектуванні REST API та способи їх уникнення: версіонування, використання DTO, підхід CQRS, робота з мікросервісами, та інші практики для підвищення продуктивності, безпеки й зручності API

    Теми цього довгочиту:

    Java
  • Java. Короткий огляд еволюції багатопотоковості

    У перших версіях Java багатопоточність реалізовувалася за допомогою класу Thread, який дозволяв створювати нові потоки. Проте ця модель мала багато недоліків:

    Теми цього довгочиту:

    Java

Вам також сподобається

Коментарі (0)

Підтримайте автора першим.
Напишіть коментар!

Вам також сподобається