BIO(阻塞式输入输出)是指在读取输入数据的时候会一直卡(阻塞)在那里,在socket编程中会导致线程无法处理其他工作,除此之外,服务端等待accept连接也是阻塞式的,所以程序想继续执行需要创建新的线程去处理其他工作。

注意socket连接并不代表连接可以被处理,连接创建后,数据处理是需要线程来工作的,当然一个进程的可连接数也不是无限大的,超过最大连接数(操作系统限制或服务端限制)会导致无法连接。

类似阻塞式的饭店,新客人来了相当于和饭店建立一个socket连接,这只是连接能不能吃饭还要等待服务员给你下单,当然饭店要是满了,新客人是进不去的。更糟糕的是,饭店只有一个服务员一直等着给第一个客人点菜下单,其他客人都不管。客人也存在着类似问题,为了和服务员维持这个连接,一直点菜或思考点什么菜,卡在那里。

----

为了解决上述线程阻塞的问题,只能创建新的线程来完成其他工作,使程序得以继续执行,下面使用两个类(BioServer,BioClient)模拟一下BIO。

public class BioServer {
  private ServerSocket serverSocket;
  public BioServer(int port,int backlog) throws IOException {
    this.serverSocket = new ServerSocket(port,backlog);
  }
  private void start() throws IOException {
    ExecutorService executorService = Executors.newFixedThreadPool(2);
    while (true) {
      Socket socket = serverSocket.accept();
      System.out.println(String.format("a new client connect %s", socket.getPort()));
      executorService.submit(new BioServerHandler(socket));
    }
  }
  public static void main(String[] args) throws IOException {
    new BioServer(6000,3).start();
  }
}
class BioServerHandler implements Runnable {
  private Socket socket;
  public BioServerHandler(Socket socket) {
    this.socket = socket;
  }
  @Override
  public void run() {
    try (BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
      PrintWriter pw = new PrintWriter(socket.getOutputStream())) {
      String msg;
      while ((msg = br.readLine()) != null) {
        System.out.println(String.format("server receive:%s", msg));
        pw.println(msg + " | " + new Date());
        pw.flush();
      }
    } catch (IOException e) {...}
  }
}

服务端使用线程池来处理客户端连接,避免accept阻塞当前线程,程序无法继续,而在单个连接的线程处理中,又存在着阻塞式的read,所以线程池中的一个线程只能为一个连接工作,无法复用(除非有连接断开线程释放)。
----
public class BioClient {
  private Socket socket;
  public BioClient(String host,int port) throws IOException {
    this.socket = new Socket(host, port);
  }
  public void start({
    new Thread(()->{
        try(BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()))){
        String msg;
        while((msg = br.readLine())!=null){
             System.out.println(String.format("client receive %s", msg));
        }
       } catch (IOException e) {...}finally {...}
     }).start();
    System.out.println("client enter msg: ");
    PrintWriter pw;
    while(true){
      try {
        pw = new PrintWriter(socket.getOutputStream());
        pw.println(new Scanner(System.in).next());
        pw.flush();
      } catch (IOException e) {...}
    }
}
  public static void main(String[] args) throws IOException {
     new BioClient("127.0.0.1",6000).start();
  }
}
客户端为了避免阻塞读取数据,创建新线程来处理数据读取工作,在当前线程阻塞式等待系统输入数据。以上就是阻塞式网络编程BIO的demo,主要存在的问题是阻塞导致线程无法复用。


©著作权归作者所有:来自51CTO博客作者mb5fdb0a4002420的原创作品,如需转载,请注明出处,否则将追究法律责任

更多相关文章

  1. 基于单节点redis实现分布式锁
  2. 基于数据库实现分布式锁
  3. 从GC的SuppressFinalize方法带你深刻认识Finalize底层运行机制
  4. 不要把异常当做业务逻辑,这性能可能你无法承受
  5. 同步异步多线程这三者关系,你能给面试官一个满意的回答吗?
  6. 多线程基础知识
  7. 【金三银四】Java并发编程面试题(2021最新版)
  8. 怎么给女朋友讲明白线程池?
  9. Java程序员除了「北上广深」其它地方能拿到30K吗?

随机推荐

  1. mac 安装Android studio遇到的问题及解决
  2. Android设备管理器漏洞
  3. Android(安卓)水波效果原理与实现
  4. 后台动态添加布局文件、控件与动态设置属
  5. Android调用系统摄像头拍照并剪裁压缩
  6. android 中文 api (43) —— Chronometer
  7. Android studio 无法启动安卓模拟器
  8. Android 百分比布局
  9. 在Android中调用动态库文件(*.so)
  10. Android TabWidget/TabHost的使用