Модель связи ввода-вывода (1) Синхронный режим блокировки BIO (блокировка ввода-вывода)

Java

Привет, мир :) Поиск WeChat " Программист Аранг«Следуйте за мной как за специалистом по техническим инструментам.подобноПосмотрите еще раз, сила безгранична.

эта статьяGitHub.com/Ниу Мух/Java…а такжеБлог программиста АрангаБыл включен, есть много точек знаний и серии статей.

несколько концепций

блокировка ввода-выводаа такженеблокирующий ввод-выводЭти две концепции находятся на программном уровне. Он в основном описывает проблему того, как программа должна реагировать, если ресурсы ввода-вывода не готовы после того, как программа запрашивает операцию ввода-вывода операционной системы: первая ждет, вторая продолжает выполняться (но использование потоков продолжает опрос до тех пор, пока ресурсы ввода-вывода не будут готовы). ).

синхронный ввод-вывода такжеАсинхронный ввод-вывод, эти две концепции относятся к уровню операционной системы. Он в основном описывает, как операционная система реагирует на программу, если ресурсы ввода-вывода не готовы после получения запроса программы на операции ввода-вывода: первая не отвечает до тех пор, пока ресурсы ввода-вывода не будут готовы; вторая возвращает флаг (чтобы программа и я знаю, куда данные будут уведомлены в дальнейшем), и когда IO-ресурс будет готов, он будет возвращен в программу с помощью механизма событий.

Синхронный режим блокировки (Blocking IO)

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

BIO Источник изображения:www.masterraghu.com

Представление псевдокода выглядит следующим образом:

{
    // 阻塞,直到有数据
	read(socket, buffer);
	process(buffer);
}

БИО связь特点:

  1. За соединение отвечает поток, а многопоточность запускает поток для каждого соединения.
  2. Запрос и ответ.
  3. Клиент ждет (блокирует), пока не ответит после запроса.

Метод связи BIO может обрабатывать только один запрос за раз на однопоточном сервере и блокируется до завершения обработки. Поэтому он не подходит для ситуаций с высоким параллелизмом. Но вы можете использовать многопоточность稍微Улучшать.

BIO通信模型-来源于慕课网

Режим синхронной блокировки Java

Режим блокировки BIO в Java находится вjava.netРеализация сокета Socket в пакете, сокет-сокет — это реализация протоколов транспортного уровня, таких как TCP/UDP.

Кодирование режима синхронной блокировки Java

многопоточный клиент

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


import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.concurrent.CountDownLatch;

/**
 * <p>
 * BIO测试
 * 模拟20个客户端并发请求,服务端则使用单线程。
 *
 * @Author niujinpeng
 * @Date 2018/10/15 10:50
 */
public class SocketClient {
    public static void main(String[] args) throws InterruptedException {
        Integer clientNumber = 20;
        CountDownLatch countDownLatch = new CountDownLatch(clientNumber);

        // 分别启动20个客户端
        for (int index = 0; index < clientNumber; index++, countDownLatch.countDown()) {
            SocketClientRequestThread client = new SocketClientRequestThread(countDownLatch, index);
            new Thread(client).start();
        }

        synchronized (SocketClient.class) {
            SocketClient.class.wait();
        }
    }
}

/**
 * <p>
 * 客户端,用于模拟请求
 *
 * @Author niujinpeng
 * @Date 2018/10/15 10:53
 */
class SocketClientRequestThread implements Runnable {

    private CountDownLatch countDownLatch;

    /**
     * 线程的编号
     */
    private Integer clientIndex;


    public SocketClientRequestThread(CountDownLatch countDownLatch, Integer clientIndex) {
        this.countDownLatch = countDownLatch;
        this.clientIndex = clientIndex;
    }

    @Override
    public void run() {
        Socket socket = null;
        OutputStream clientRequest = null;
        InputStream clientResponse = null;
        try {
            socket = new Socket("localhost", 83);
            clientRequest = socket.getOutputStream();
            clientResponse = socket.getInputStream();

            //等待,直到SocketClientDaemon完成所有线程的启动,然后所有线程一起发送请求
            this.countDownLatch.await();

            // 发送请求信息
            clientRequest.write(("这是第" + this.clientIndex + "个客户端的请求").getBytes());
            clientRequest.flush();

            // 等待服务器返回消息
            System.out.println("第" + this.clientIndex + "个客户端请求发送完成,等待服务器响应");
            int maxLen = 1024;
            byte[] contentBytes = new byte[maxLen];
            int realLen;
            String message = "";

            // 等待服务端返回,in和out不能cloese
            while ((realLen = clientResponse.read(contentBytes, 0, maxLen)) != -1) {
                message += new String(contentBytes, 0, realLen);
            }
            System.out.println("第" + this.clientIndex + "个客户端接受到来自服务器的消息:" + message);

        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            try {
                if (clientRequest != null) {
                    clientRequest.close();
                }
                if (clientRequest != null) {
                    clientResponse.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

однопоточный сервер

Поскольку Socket в Java — это режим BIO, мы можем легко написать однопоточный сервер BIO.

SocketServer.java


import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * BIO服务端
 * <p>
 * 单线程阻塞的服务器端
 *
 * @Author niujinpeng
 * @Date 2018/10/15 11:17
 */
public class SocketServer {

    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(83);
        try {
            while (true) {
                // 阻塞,直到有数据准备完毕
                Socket socket = serverSocket.accept();

                // 开始收取信息
                InputStream input = socket.getInputStream();
                OutputStream output = socket.getOutputStream();
                Integer sourcePort = socket.getPort();
                int maxLen = 1024 * 2;
                byte[] contextBytes = new byte[maxLen];

                // 阻塞,直到有数据准备完毕
                int realLen = input.read(contextBytes, 0, maxLen);
                // 读取信息
                String message = new String(contextBytes, 0, realLen);

                // 输出接收信息
                System.out.println("服务器收到来自端口【" + sourcePort + "】的信息:" + message);
                // 响应信息
                output.write("Done!".getBytes());

                // 关闭
                output.close();
                input.close();
                socket.close();

            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (serverSocket != null) {
                serverSocket.close();
            }
        }
    }
}

многопоточный сервер

Однопоточный сервер может обрабатывать только один запрос одновременно, то есть, если обнаружено, что запрос не был обработан, когда запрос поступает, он может только ждать обработки, поэтому используйте多线程改进Сервер.

SocketServerThread.java


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * BIO服务端
 * <p>
 * 多线程的阻塞的服务端
 * <p>
 * 当然,接收到客户端的socket后,业务的处理过程可以交给一个线程来做。
 * 但还是改变不了socket被一个一个的做accept()的情况。
 *
 * @Author niujinpeng
 * @Date 2018/10/15 11:17
 */
public class SocketServerThread implements Runnable {

    /**
     * 日志
     */
    private static final Logger logger = LoggerFactory.getLogger(SocketServerThread.class);

    private Socket socket;

    public SocketServerThread(Socket socket) {
        this.socket = socket;
    }

    public static void main(String[] args) throws Exception {
        ServerSocket serverSocket = new ServerSocket(83);
        try {
            while (true) {
                Socket socket = serverSocket.accept();
                //当然业务处理过程可以交给一个线程(这里可以使用线程池),并且线程的创建是很耗资源的。
                //最终改变不了.accept()只能一个一个接受socket的情况,并且被阻塞的情况
                SocketServerThread socketServerThread = new SocketServerThread(socket);
                new Thread(socketServerThread).start();
            }
        } catch (Exception e) {
            System.out.println(e.getMessage());
        } finally {
            if (serverSocket != null) {
                serverSocket.close();
            }
        }
    }


    @Override
    public void run() {
        InputStream in = null;
        OutputStream out = null;
        try {
            //下面我们收取信息
            in = socket.getInputStream();
            out = socket.getOutputStream();
            Integer sourcePort = socket.getPort();
            int maxLen = 1024;
            byte[] contextBytes = new byte[maxLen];
            //使用线程,同样无法解决read方法的阻塞问题,
            //也就是说read方法处同样会被阻塞,直到操作系统有数据准备好
            int realLen = in.read(contextBytes, 0, maxLen);
            //读取信息
            String message = new String(contextBytes, 0, realLen);

            //下面打印信息
            logger.info("服务器收到来自于端口:" + sourcePort + "的信息:" + message);

            //下面开始发送信息
            out.write("回发响应信息!".getBytes());
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        } finally {
            //试图关闭
            try {
                if (in != null) {
                    in.close();
                }
                if (out != null) {
                    out.close();
                }
                if (this.socket != null) {
                    this.socket.close();
                }
            } catch (IOException e) {
                logger.error(e.getMessage(), e);
            }
        }
    }
}

Кажется, что многопоточность увеличивает пропускную способность сервиса, но видно, что после улучшений многопоточности остаются следующие局限性:

  • Процесс получения и уведомления результатов обработки по-прежнему является однопоточным.
  • Система может создавать ограниченное количество потоков.cat /proc/sys/kernel/threads-maxВы можете просмотреть количество потоков, которые могут быть созданы.
  • Если потоков больше, процессору требуется больше времени для переключения и меньше времени для обработки реального бизнеса.
  • Создание потока требует больше ресурсов, а JVM по умолчанию выделяет 128 КБ пространства при создании потока.
  • Многопоточность также не может решить эту проблему, потому что调用底层系统из同步IOИ синхронный IO механизм решения.

Сводка синхронного режима блокировки

Режим BIO не потребляет слишком много ресурсов ЦП из-за блокировки и приостановки процесса, а сложность разработки низкая, что больше подходит для разработки сетевых приложений с небольшим параллелизмом. В то же время время от времени легко найти приложения с большим объемом параллелизма, потому что запрос ввода-вывода блокирует процесс. Если вы выделяете поток для каждого запроса, системные накладные расходы будут слишком большими.

В то же время в Java для борьбы с режимом блокировки используется многопоточность, и невозможно решить проблему, в которой находится программа.accept()а такжеread()проблема блокировки времени. потому чтоaccept()а такжеread()Поддержка режима IO основана на операционной системе, если операционная система не найдена сокета, поступила из указанного передачи портов, затем操作系统就会等待. такaccept()а такжеread()метод будет ждать вечно.


Источник на гитхабе:GitHub.com/Ниу Мух/Java…

Ссылки на эту статью:5 моделей ввода-вывода, блокирующий ввод-вывод и неблокирующий ввод-вывод, синхронный ввод-вывод и асинхронный ввод-выводСсылки на эту статью:Проектирование архитектуры: связь между системами (3) — модель связи ввода-вывода и практика JAVA, часть 1

Привет, мир :) Я Аланг, передовой специалист по техническим инструментам, серьезно пишу статьи.

Нравятся комментариивсе ониталант, не только выглядит красивым и симпатичным, но и красиво говорит.

Статья постоянно обновляется, вы можете искать в WeChat " Программист Аранг"или посетите"Блог программиста Аранга"Читал в первый раз.

эта статьяGitHub.com/Ниу Мух/Java…Он был включен, есть много знаний и серии статей, добро пожаловать в Star.