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