博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java中Socket阻塞的原因
阅读量:4305 次
发布时间:2019-05-27

本文共 2188 字,大约阅读时间需要 7 分钟。

Java中Socket阻塞的原因

对于用ServerSocket 及 Socket 编写的服务器程序和客户程序, 他们在运行过程中常常会阻塞. 例如, 当一个线程执行 ServerSocket 的accept() 方法时, 假如没有客户连接, 该线程就会一直等到有客户连接才从 accept() 方法返回. 再例如, 当线程执行 Socket 的 read() 方法时, 如果输入流中没有数据, 该线程就会一直等到读入足够的数据才从 read() 方法返回.
      假如服务器程序需要同时与多个客户通信, 就必须分配多个工作线程, 让他们分别负责与一个客户通信, 当然每个工作线程都有可能经常处于长时间的阻塞状态.
      从 JDK1.4 版本开始, 引入了非阻塞的通信机制. 服务器程序接收客户连接, 客户程序建立与服务器的连接, 以及服务器程序和客户程序收发数据的操作都可以按非阻塞的方式进行. 服务器程序只需要创建一个线程, 就能完成同时与多个客户通信的任务.
      非阻塞的通信机制主要由 java.nio 包(新I/O包) 中的类实现, 主要的类包括 ServerSocketChannel, SocketChannel, Selector, SelectionKey 和 ByteBuffer 等.
     
一. 线程阻塞的概念
      在生活中, 最常见的阻塞现象是公路上汽车的堵塞. 汽车在公路上快速行驶, 如果前方交通受阻, 就只好停下来等待, 等到交通畅顺, 才能恢复行驶.
      线程在运行中也会因为某些原因而阻塞. 所有处于阻塞状态的线程的共同特征是: 放弃CPU, 暂停运行, 只有等到导致阻塞的原因消除, 才能恢复运行; 或者被其他线程中断, 该线程会退出阻塞状态, 并且抛出 InterruptedException.
1.1 线程阻塞的原因
      导致线程阻塞的原因主要有以下几方面.
线程执行了 Thread.sleep(int n) 方法, 线程放弃 CPU, 睡眠 n 毫秒, 然后恢复运行.

线程要执行一段同步代码, 由于无法获得相关的同步锁, 只好进入阻塞状态, 等到获得了同步锁, 才能恢复运行.

线程执行了一个对象的 wait() 方法, 进入阻塞状态, 只有等到其他线程执行了该对象的 notify() 和 notifyAll() 方法, 才可能将其呼醒.

线程执行 I/O 操作或进行远程通信时, 会因为等待相关的资源而进入阻塞状态. 例如, 当线程执行 System.in.read() 方法时, 如果用户没有向控制台输入数据, 则该线程会一直等读到了用户的输入数据才从 read() 方法返回.

进行远程通信时, 在客户程序中, 线程在以下情况可能进入阻塞状态.

请求与服务器建立连接时, 即当线程执行 Socket 的带参数构造方法, 或执行 Socket 的 connect() 方法时, 会进入阻塞状态, 直到连接成功, 此线程才从 Socket 的构造方法或 connect() 方法返回.

线程从 Socket 的输入流读入数据时, 如果没有足够的数据, 就会进入阻塞状态, 直到读到了足够的数据, 或者到达输入流的末尾, 或者出现了异常, 才从输入流的 read() 方法返回或异常中断. 输入流中有多少数据才算足够呢? 这要看线程执行的 read() 方法的类型.

> int read(): 只要输入流中有一个字节, 就算足够.

> int read( byte[] buff): 只要输入流中的字节数目与参数buff 数组的长度相同, 就算足够.
> String readLine(): 只要输入流中有一行字符串, 就算足够. 值得注意的是, InputStream 类并没有 readLine() 方法, 在过滤流 BufferedReader 类中才有此方法.
线程向 Socket 的输出流写一批数据时, 可能会进入阻塞状态, 等到输出了所有的数据, 或者出现异常, 才从输出流 的 write() 方法返回或异常中断.

调用 SOcket 的setSoLinger() 方法设置了关闭 Socket 的延迟时间, 那么当线程执行 Socket 的 close() 方法时, 会进入阻塞状态, 直到底层 Socket 发送完所有剩余数据, 或者超过了 setSoLinger() 方法设置的延迟时间, 才从 close() 方法返回.

在服务器程序中, 线程在以下情况下可能会进入阻塞状态.

线程执行 ServerSocket 的 accept() 方法, 等待客户的连接, 直到接收到了客户连接, 才从 accept() 方法返回.    

线程从 Socket 的输入流读入数据时, 如果输入流没有足够的数据, 就会进入阻塞状态.

线程向 Socket 的输出流写一批数据时, 可能会进入阻塞状态, 等到输出了所有的数据, 或者出现异常, 才从输出流的 write() 方法返回或异常中断.

由此可见, 无论在服务器程序还是客户程序中, 当通过 Socket 的输入流和输出流来读写数据时, 都可能进入阻塞状态. 这种可能出现阻塞的输入和输出操作被称为阻塞 I/O. 与此对照, 如果执行输入和输出操作时, 不会发生阻塞, 则称为非阻塞 I/O.

你可能感兴趣的文章
cant connect to local MySQL server through socket /tmp/mysql.sock (2)
查看>>
vue中的状态管理 vuex store
查看>>
Maven之阿里云镜像仓库配置
查看>>
Maven:mirror和repository 区别
查看>>
微服务网关 Spring Cloud Gateway
查看>>
SpringCloud Feign的使用方式(一)
查看>>
SpringCloud Feign的使用方式(二)
查看>>
关于Vue-cli+ElementUI项目 打包时排除Vue和ElementUI
查看>>
Vue 路由懒加载根据根路由合并chunk块
查看>>
vue中 不更新视图 四种解决方法
查看>>
MySQL 查看执行计划
查看>>
OpenGL ES 3.0(四)图元、VBO、VAO
查看>>
OpenGL ES 3.0(五)纹理
查看>>
OpenGL ES 3.0(八)实现带水印的相机预览功能
查看>>
OpenGL ES 3.0(九)实现美颜相机功能
查看>>
FFmpeg 的介绍与使用
查看>>
Android 虚拟机简单介绍——ART、Dalvik、启动流程分析
查看>>
原理性地理解 Java 泛型中的 extends、super 及 Kotlin 的协变、逆变
查看>>
FFmpeg 是如何实现多态的?
查看>>
FFmpeg 源码分析 - avcodec_send_packet 和 avcodec_receive_frame
查看>>