BIO/NIO/AIO

本文探讨了BIO、NIO和AIO在不同架构中的应用,重点介绍了AIO在高并发相册服务器中的优势,并剖析了伪异步服务端使用线程池的优点和局限性。通过实例展示了如何利用线程池避免线程耗尽,但指出其并未根本解决同步问题和队列阻塞。
摘要由CSDN通过智能技术生成

BIO:同步并阻塞,一般运用到连接数目少,固定的架构。这种方式对服务其资源要求比较高。

NIO:同步非阻塞,一般运用到连接数目多,数据比较短的架构。一般用在聊天室。

AIO:异步非阻塞,一般运用到连接数目多,数据比较长的架构。比如相册服务器,充分调用OS参与并发操作。

—————————————————————————————————————————————————————————

BIO:同步并阻塞

客户端:
socket
1.Socket对象请求与服务的连接
2.从Socket管道中获取输入、输出流读写数据
服务端:
Socket	
1.通过ServerSocket注册端口
2.服务端通过调用accept方法用于监听客户端的Socket请求
3从Socket中获取字节输入或者输出流进行数据的读写操作
服务端
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * 服务端接受消息
 */
public class Server {
    public static void main(String[] args) throws IOException {
        //1.定义一个ServerSocket对象进行服务端的端口注册
        ServerSocket serverSocket = new ServerSocket(9999);
        //2.监听客户端的连接请求
        Socket socket = serverSocket.accept();
        //3.从socket管道中得到字节输入流对象
        InputStream inputStream = socket.getInputStream();
        //4.把字节输入流转成字符输入流,然后包装成一个缓冲字符输入流
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
        String msg;
        while ((msg = bufferedReader.readLine()) != null) {
            System.out.println("服务端接收到:" + msg);
        }
    }
}
客户端
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.Socket;

/**
 * 客户端发送消息
 */
public class Client {
    public static void main(String[] args) throws IOException {
        //1.创建一个socket对象请求服务端的链接
        Socket socket = new Socket("127.0.0.1", 9999);
        //2.从socket对象中获取一个字节输出流
        OutputStream outputStream = socket.getOutputStream();
        //3.把字节输出流包装成一个打印流
        PrintStream printStream = new PrintStream(outputStream);
        //4.把数据打印出去
        printStream.print("hellp world!服务端,你好!");
        printStream.close();
    }
}

服务端持续接受消息
package com.example2;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * 服务端持续接受消息
 */
public class Server {
    public static void main(String[] args) throws IOException {
        //1.定义一个ServerSocket对象及逆行服务端的端口注册
        ServerSocket serverSocket = new ServerSocket(9999);
        //2.监听客户端的socket连接请求
        Socket socket = serverSocket.accept();
        //3.从socket管道中得到一个字节输入流
        InputStream inputStream = socket.getInputStream();
        //4.把字节输入流转为为字符输入流,然后包装成缓存字符流
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
        String msg;
        while ((msg = bufferedReader.readLine()) != null) {
            System.out.println("服务端接受到:" + msg);
        }
    }
}
客户端持续发送消息
package com.example2;

import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Scanner;

/**
 * 客户端持续发送消息
 */
public class Client {
    public static void main(String[] args) throws IOException {
        //1。创建socket对象请求服务端的链接
        Socket socket = new Socket("127.0.0.1", 9999);
        //2.从socket对象中获取一个字节输出流
        OutputStream outputStream = socket.getOutputStream();
        //3.把输出字节流包装成打印流
        PrintStream printStream = new PrintStream(outputStream);
        Scanner scanner = new Scanner(System.in);
        while (true){
            System.out.println("请说:");
            String msg = scanner.nextLine();
            printStream.println(msg);
            printStream.flush();
        }
    }
}

服务端实现接收多客户端消息
package com.example3;

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

/**
 * 服务端可以同时接收多个客户端的socket通信请求
 * 当服务端接收到客户端的socket请求对象时,都交给一个独立的线程来处理客户端的数据交互请求
 */
public class Server {
    public static void main(String[] args) throws IOException {
        //1。注册端口
        ServerSocket serverSocket = new ServerSocket(9999);
        //2.定义一个死循环,负责不断接受客户端的socket链接请求
        while (true){
            Socket socket = serverSocket.accept();
            //3.创建一个独立的线程来处理与这个客户端的socket通信需求
            new ServerThreadReader(socket).start();
        }
    }
}
服务端多线程类
package com.example3;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.Socket;

public class ServerThreadReader extends Thread {
    private Socket socket;

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

    @Override
    public void run() {
        try {
            //1.从socket对象中得到一个字节输入流
            InputStream inputStream = socket.getInputStream();
            //2.把字节输入流转为字符输入流,然后包装成缓存字符流
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
            String msg;
            while ((msg = bufferedReader.readLine()) != null) {
                 System.out.println("接受到客户端消息:" + msg);
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
多个客户端给服务端发送消息
package com.example3;

import java.io.IOException;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Scanner;

/**
 * 客户端发送消息
 */
public class Client {
    public static void main(String[] args) throws IOException {
        //1。请求与服务端的socket对象链接
        Socket socket = new Socket("127.0.0.1",9999);
        //2.得到打印流
        PrintStream printStream = new PrintStream(socket.getOutputStream());
        //3.使用循环不断的发送小徐给服务端接收
        Scanner scanner = new Scanner(System.in);
        while (true){
            System.out.println("请说:");
            String msg = scanner.nextLine();
            printStream.println(msg);
            printStream.flush();
        }
    }
}
1.每个socket接收到,都会创建一个线程,线程的竞争、切换上下文影响性能
2.每个线程都会占用空间和CPU资源
3.并不是每个socket都进行IO操作,无意义的线程处理
4.客户端的并发访问增加时,服务端将呈现1:1的线程开销,访问量越大,系统就越可能崩溃死机

伪异步服务端
package com.example4;

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

/**
 * 开发伪异步通信框架
 */
public class Server {
    public static void main(String[] args) throws IOException {
        //1.注册端口
        ServerSocket serverSocket = new ServerSocket(9999);
        //2.初始化一个线程池对象
        HandlerSocketServerPool handlerSocketServerPool = new HandlerSocketServerPool(6, 10);
        //3.定义一个循环接收客户端socket连接请求
        while (true) {
            Socket socket = serverSocket.accept();
            //4.把socket包装成任务
            Runnable target = new ServerRunnableTarget(socket);
            //5.把target任务对象交给一个线程池进行处理
            handlerSocketServerPool.execute(target);
        }
    }
}
服务端线程池
package com.example4;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class HandlerSocketServerPool {
    //1.创建1个线程池的成员变量。用于存储一个线程池对象
    private ExecutorService executorService;

    //2创建这个类的对象的时候就需要初始化线程池对象
    public HandlerSocketServerPool(int maxThreadNum, int queueSize) {
        executorService = new ThreadPoolExecutor(3, maxThreadNum, 120, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(queueSize));
    }
    //3.提供一个方法来提交任务给线程池的任务队列来暂存,等着线程池来处理
    public void execute(Runnable target){
        executorService.execute(target);
    }
}
伪异步线程任务runnable
package com.example4;

import java.io.*;
import java.net.Socket;

public class ServerRunnableTarget implements Runnable{
    private Socket socket;
    public ServerRunnableTarget(Socket socket){
        this.socket = socket;
    }
    @Override
    public void run() {
        try {
            //1.从管道中得到1个字节流
            InputStream inputStream = socket.getInputStream();
            //2.把字节流包装成缓冲字符流
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
            String msg;
            while ((msg = bufferedReader.readLine())!=null){
                System.out.println("接收到客户端信息:"+msg);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
客户端
package com.example4;

import java.io.IOException;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Scanner;

/**
 * 客户端发送消息
 */
public class Client {
    public static void main(String[] args) throws IOException {
        //1。请求与服务端的socket对象链接
        Socket socket = new Socket("127.0.0.1",9999);
        //2.得到打印流
        PrintStream printStream = new PrintStream(socket.getOutputStream());
        //3.使用循环不断的发送小徐给服务端接收
        Scanner scanner = new Scanner(System.in);
        while (true){
            System.out.println("请说:");
            String msg = scanner.nextLine();
            printStream.println(msg);
            printStream.flush();
        }
    }
}
1.伪异步io采用了线程池实现,因此避免了为每个请求创建一个独立线程造成线程资源耗尽问题
2.由于底层依然使用同步并阻塞模型,因此无法从根本上解决问题
3.如果单个消息处理的缓慢,或者服务器线程池中的全部线程都被阻塞,那么后续socket的io消息都在队列排队。
新的socket请求将被拒绝,客户端会发生大量连接超时。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值