Question:
I'm trying to understand what asynchrony is. Previously, I watched several articles / videos about this, but with each subsequent viewing, I got completely confused. One video said that
threads are like additional workers, but their problem is that while one thread is running, others are waiting for its end.
asynchrony is the ability to give commands to execute a certain when, and at this moment it can perform its tasks in parallel.
I'm stuck on this explanation. When I wrote the server\client. The server organized several threads that ran in parallel. I had a method in the left thread for the number of clients connected to the server, a thread for connecting new clients, and the main thread.
Maybe I'm missing something. Maybe it's all happening too fast and you don't notice it.
Basically, please explain.
Answer:
First of all, you need to understand the terminology. In its simplest form, a program executes sequentially or synchronously, instruction by instruction, in the order they appear in the code.
System.out.println("A");
System.out.println("B");
System.out.println("C");
Each call to println
blocking . This means that until it ends, the program will not be executed further.
In the real world, a program is required to respond to many events occurring at random times, or simultaneously perform tasks that are significantly longer than displaying on the screen. Such execution is called multitasking . But this is where confusion often arises, because the term " asynchronous execution" is often used instead, especially in the Java world, and this term has its own meaning, more specific.
To better understand the true meaning of the terms, I will give a household analogy:
-
One person puts a pot on the stove, waits for it to boil, throws in dumplings, waits for it to cook, removes it, puts in a second one, waits for it to boil … – sequential single-threaded execution.
-
One person puts two pots on the stove, as soon as one of them boils, throws dumplings, etc. – asynchronous execution.
-
Two people put two pots… – multi-threaded execution.
In this analogy, the stove is a hardware resource (processor), the dumplings are a software resource (sockets), the person is a thread, and the boil is a blocking call.
Note:
To the analogy, we can add another option "Two people put two pans on two stoves", which describes the actual parallelism in a multiprocessor system. But with the description of the types of multitasking, you can easily go beyond the limit on the number of characters per answer.
Obviously, with asynchronous execution, fewer resources are required, and the speed is the same as with multi-threaded execution. That is why the c10k problem can only be solved by asynchronous servers. The downside is that asynchronous code is often more complex and not everything can be done asynchronously.
In your last question, I already showed examples of multi-threaded and asynchronous code:
Multithreaded code
ExecutorService executor = Executors.newFixedThreadPool(10);
while (true) {
// Вызов accept() блокирующийся и остановит цикл до тех пор
// пока не поступит новое соединение
SocketChannel socketChannel = serverSocketChannel.accept();
// При этом будет запущен отдельный поток, который будет
// обслуживать только это соединение
executor.execute(() -> {
// Отправка файл, которая займёт 5 секунд например
});
// Выполнение продолжится сразу после вызова execute,
// меньше чем через миллисекунду. Если подключится ещё один
// клиент, для него будет запущен ещё один поток, и будут
// передаваться два файла одновременно двум разным клиентам.
}
Asynchronous code
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();
}
}
In the asynchronous code example, there is not a single blocking call, the loop never stops. Which multitasking option to choose is a question that makes sense only in the context of a specific task. Each option has its pros and cons. It can be seen from the examples that more asynchronous code is required, but with asynchronous execution, a situation arises much less often when two people fight over one empty pan while dumplings burn out in another.