浅谈同步与异步、阻塞非阻塞、BIO(demo)

一、 同步与异步:

用户线程和内核的交互方式
同步:用户线程发起IO操作需要等待或者轮询内核是否完成IO操作
异步:用户线程发起IO操作后无需等待,可以执行其它操作

二、 阻塞与非阻塞:

用户线程调用内核IO操作时的状态
阻塞:用户线程调用内核IO后被挂起
非阻塞:用户线程调用IO后直接返回状态,回调函数通知

一个例子(引自知乎):
1.老张把水壶放到火上,立等水开。(同步阻塞)

2.老张把水壶放到火上,去客厅看电视,时不时去厨房看看水开没有。(同步非阻塞)

买了把会响笛的那种水壶。水开之后,能大声发出嘀~~~~的噪音。

3.老张把响水壶放到火上,立等水开。(异步阻塞)

老张觉得这样傻等意义不大

4.老张把响水壶放到火上,去客厅看电视,水壶响之前不再去看它了,响了再去拿壶。(异步非阻塞)

所谓同步异步,只是对于水壶而言。
普通水壶,同步;响水壶,异步。
虽然都能干活,但响水壶可以在自己完工之后,提示老张水开了。这是普通水壶所不能及的。
同步只能让调用者去轮询自己(情况2中),造成老张效率的低下,cpu密集型。
所谓阻塞非阻塞,仅仅对于老张而言。
立等的老张,阻塞;看电视的老张,非阻塞。

简单的说:在处理IO的时候,阻塞和非阻塞都是同步IO。
只有使用了特殊的API才是异步IO。

三、socket

套接字,封装通信协议为网络通信提供接口,两台主机之间逻辑连接的端点,一般封装ip,端口号,协议

java网络通信简单流程

  • 服务端实力化ServerSocket对象(绑定端口)

  • 服务端调用accept()方法监听客户端的请求

  • 服务端等待时,客户端实例化一个socket对象,指定服务器名称和端口号连接

  • 连接建立成功,服务端会返回一个新的socket,用来和客户端进行通信

四、BIO

在BIO中,每次建立连接后,当线程调用read or write方法时,线程会被阻塞,直到有数据可读可写, 在此期间线程不能做其它事情。并且每次建立一个连接,都会创建一个线程进行读写,创建线程是需要消耗系统资源,在连接过多时,会撑爆系统。

BIO代码:
客户端:

public class Client {
    private  int port = 12345;
    private static String serverIp = "localhost";

    public Client(int port) {
        this.port = port;
    }

    public void send() {
        Scanner sc = new Scanner(System.in);
        try (Socket socket = new Socket(serverIp, port);
             //创建流
             BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
             PrintWriter out = new PrintWriter(socket.getOutputStream(), true)) {

            System.out.println("请输入一个数字:");
            int a = sc.nextInt();
            //向服务端写入数据
            out.println(a);
            //读取服务端返回结果
            System.out.println("服务端结果为:" + in.readLine());

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

服务端

public class Server {
    //单例
    private static ServerSocket serverSocket;

    public synchronized static void start(int port) throws IOException {

        if(serverSocket != null) return;

        try {
            serverSocket = new ServerSocket(port);
            System.out.println("服务器已启动,端口号:" + port);

            Socket socket;
            //死循环监听
            while (true) {
                //监听到客户端通信建立连接,完成三次握手
                socket = serverSocket.accept();
                //每次建立连接开启线程处理
                new Thread(new ServerHandle(socket)).start();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally{
            //一些必要的清理工作
            if(serverSocket != null){
                System.out.println("服务器已关闭。");
                serverSocket.close();
                serverSocket = null;
            }
        }
    }
}

测试通信:

public class Test {
    public static void main(String[] args) throws InterruptedException {
        //运行服务器
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    //ServerForPool.start(12345);
                    Server.start(12345);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }).start();

        Thread.sleep(100);

        Client client = new Client(12345);
        client.send();
    }
}

上面说了这种情况非常消耗资源,可以稍微改进一下,加个线程池,这样就不会无限创建线程处理连接:

public class ServerForPool {
    private static int port = 12345;
    private static ServerSocket serverSocket;

    private static ExecutorService executorService = Executors.newFixedThreadPool(60);

    public synchronized static void start(int port) throws IOException {
        if(serverSocket != null)
            return;
        try {
            serverSocket = new ServerSocket(port);

            System.out.println("服务器已启动:" + port);

            Socket socket;
            while (true) {
                socket = serverSocket.accept();
                executorService.execute(new ServerHandle(socket));
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(serverSocket != null){
                System.out.println("服务器已关闭。");
                serverSocket.close();
                serverSocket = null;
            }
        }
    }
}

猜你喜欢

转载自blog.csdn.net/LJJZJ/article/details/87890024
今日推荐