Страницы

Поиск по вопросам

четверг, 19 декабря 2019 г.

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

#java #многопоточность #async


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

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

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

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

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

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


Ответы

Ответ 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 keys = selector.selectedKeys(); Iterator 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(); } } В примере асинхронного кода нет ни одного блокирующегося вызова, цикл никогда не останавливается. Какой вариант многозадачности выбрать - это вопрос имеющий смысл только в контексте конкретной задачи. У каждого варианта свои плюсы и минусы. Из примеров видно, что асинхронного кода требуется больше, но при асинхронном выполнении значительно реже возникает ситуация, когда два человека дерутся за одну пустую кастрюлю, пока в другой догорают пельмени.

Комментариев нет:

Отправить комментарий