Многопоточность и ассинхронность

232
20 апреля 2018, 17:18

Добрый день, пытаюсь понять что такое асинхронность. Ранее смотрел несколько статей\видео по этому поводу, но с каждым последующим просмотром - совсем запутался. В одном видео говорил про то что

потоки - это подобие дополнительных рабочих, но их проблема в том что пока работает один поток другие ожидают его окончание.

асинхронность - это возможность давать команды на выполнение определенного когда,и в этот момент может паралельно выполнять свои задачи.

На этом объяснении я завис. Когда я писал сервер\ клиент. В сервере организовывал несколько потоков которые выполнялись паралельно. У меня был метод в левом потоке на количество клиентов подключенных к серверу, поток для подключения новых клиентов, и главный поток.

Возможно я чего-то не доганяю. Может это все происходит слишком быстро и это не замечается.

В общем, объясните пожалуйста

Answer 1

Прежде всего надо разобраться с терминологией. В самом простом виде программа выполняет последовательно или синхронно, инструкция за инструкцией, в том порядке, в котором они идут в коде.

System.out.println("A");
System.out.println("B");
System.out.println("C");

Каждый вызов println блокирующийся. Это значит, что пока он не закончится, программа не будет выполняться дальше.

В реальном мире от программы требуется реагировать на множество событий, происходящих в произвольные моменты времени, или одновременно выполнить задачи существенно более долгие, чем вывод на экран. Такое выполнение называется многозадачным. Но в этом месте часто возникает путаница, так как вместо этого часто, особенно в мире Java, используют термин "асинхронное выполнение", а у этого термина есть своё значение, более конкретное.

Чтобы лучше разобраться в истинном значении терминов, приведу бытовую аналогию:

  • Один человек ставит кастрюлю на плиту, ждёт пока она закипит, закидывает пельмени, ждёт пока сварятся, снимает, ставит вторую, ждёт пока закипит... - последовательное однопоточное выполнение.

  • Один человек ставит две кастрюли на плиту, как только одна из них закипела, забрасывает пельмени и т.д. - асинхронное выполнение.

  • Два человека ставят две кастрюли... - многопоточное выполнение.

В данной аналогии плита - это аппаратный ресурс (процессор), пельмени - программный ресурс (сокеты), человек - это поток, а кипение - блокирующийся вызов.

Примечание:
К аналогии можно добавить ещё вариант "Два человека ставят две кастрюли на две плиты", описывающий действительную параллельность в многопроцессорной системе. Но с описанием видов многозадачности можно легко выйти за ограничение по количеству символов на ответ.

Очевидно, что при асинхронном выполнении ресурсов требуется меньше, а скорость та же, что и при многопоточном. Именно поэтому проблему c10k могут решать только асинхронные сервера. Минусы в том, что асинхронный код зачастую сложнее и не всё можно выполнять асинхронно.

В вашем прошлом вопросе я уже показывал примеры многопоточного и асинхронного кода:

Многопоточный код

ExecutorService executor = Executors.newFixedThreadPool(10);
while (true) {
    // Вызов accept() блокирующийся и остановит цикл до тех пор
    // пока не поступит новое соединение
    SocketChannel socketChannel = serverSocketChannel.accept();
    // При этом будет запущен отдельный поток, который будет
    // обслуживать только это соединение
    executor.execute(() -> {
        // Отправка файл, которая займёт 5 секунд например
    });
    // Выполнение продолжится сразу после вызова execute,
    // меньше чем через миллисекунду. Если подключится ещё один
    // клиент, для него будет запущен ещё один поток, и будут
    // передаваться два файла одновременно двум разным клиентам.
}

Асинхронный код

serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
while (true) {  // Цикл событий
    selector.select();  // Мультиплексирование
    Set<SelectionKey> keys = selector.selectedKeys();
    Iterator<SelectionKey> iterator = keys.iterator();
    while (iterator.hasNext()) {
        SelectionKey key = iterator.next();
        if (key.isAcceptable()) {  // Поступило новое соединение
            SocketChannel socketChannel = serverSocketChannel.accept();
            socketChannel.configureBlocking(false);
            // Регистрируем его для обработки в этом же потоке
            // на следующих итерациях цикла
            socketChannel.register(selector, SelectionKey.OP_READ, new Storage());
        }
        if (key.isReadable()) {  // В одном из соединений появились данные
            SocketChannel socketChannel = (SocketChannel) key.channel();
            ByteBuffer buffer = ByteBuffer.allocate(256);
            // Читаем столько данных, сколько сейчас есть
            int read = socketChannel.read(buffer);
            if (read != -1) {
                ...
            }
            else {  // Ничего не прочиталось, значит пора закрывать соединение
                socketChannel.close();
            }
        }
        iterator.remove();
    }
}

В примере асинхронного кода нет ни одного блокирующегося вызова, цикл никогда не останавливается. Какой вариант многозадачности выбрать - это вопрос имеющий смысл только в контексте конкретной задачи. У каждого варианта свои плюсы и минусы. Из примеров видно, что асинхронного кода требуется больше, но при асинхронном выполнении значительно реже возникает ситуация, когда два человека дерутся за одну пустую кастрюлю, пока в другой догорают пельмени.

READ ALSO
DocFlavor.BYTE_ARRAY.AUTOSENSE не учитывает PrintRequestAttributeSet

DocFlavor.BYTE_ARRAY.AUTOSENSE не учитывает PrintRequestAttributeSet

Добрый день, не учитывает атрибуты из PrintRequestAttributeSet, если в DocFlavor Установлен AUTOSENSE

197
где можно найти хорошие уроки по SceneBuilder?

где можно найти хорошие уроки по SceneBuilder?

Создать саму обертку легко, а где можно научиться связыванию логики и этой обертки? Может учебники есть, или видеоуроки

156
Получение поля из DataSnapshot | Firebase-android

Получение поля из DataSnapshot | Firebase-android

Выполняю запрос к своей базе данных Firebase:

186
Регулярные выражения, замена символов

Регулярные выражения, замена символов

Мне нужно убирать из строки ненужные символы методом replaceAll(String regex, String replacement)Например строка "Hel

172