`
春花秋月何时了
  • 浏览: 41842 次
  • 性别: Icon_minigender_1
  • 来自: 成都
社区版块
存档分类
最新评论

Java传统IO

阅读更多

传统IO有两种形式,一种是阻塞IO,另一种是阻塞IO+每个请求创建线程/线程池。

阻塞IO

IO的阻塞、非阻塞主要表现在一个IO操作过程中,如果有些操作很慢,比如读操作时需要准备数据,那么当前IO进程是否等待操作完成,还是得知暂时不能操作后先去做别的事情?一直等待下去,什么事也不做直到完成,这就是阻塞。抽空做些别的事情,这是非阻塞。

在传统IO里,InputStream.read()方法时是阻塞的,它会一直等到数据到来时(或超时)才会返回;同样,在网络IO运用中调用ServerSocket.accept()方法时,也会一直阻塞到有客户端连接才会返回。

 以网络IO操作为例:

public static void main(String[] args) {
        Socket socket = null;
        try {
            ServerSocket ss = new ServerSocket(10000);
            while (true) {
                socket = ss.accept();//阻塞
                BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                String msg = null;
                while((msg = in.readLine()) != null){//readLine阻塞方法
                    System.out.println(msg);
                }
            }

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

 这种阻塞IO的缺陷如下:

  1. 在调用InputStream.read()/buffer.readLine()方法时是阻塞的,它会一直等到数据到来或缓冲区已满时或超时时才会返回,并且产生了大量String类型垃圾,尽管可以使用StringBuffer优化。
  2. 在调用ServerSocket.accept()方法时,也会一直阻塞到有客户端连接才会返回。
  3. 由于服务器端是单线程的,在第一个连接的客户端阻塞了线程后,第二个客户端必须等待第一个断开后才能连接。
  4. 所有的客户端连接在请求服务端时都会阻塞住,等待前面的完成。即使是使用短连接,数据在写入 OutputStream 或者从 InputStream 读取时都有可能会阻塞。这在大规模的访问量或者系统对性能有要求的时候是不能接受的。

阻塞IO + 每个请求创建线程

为每个客户端请求创建单独的线程是对传统的阻塞式IO最常见的解决办法,示例如下:

public static void main(String[] args) {
        try {
            ServerSocket ss = new ServerSocket(10000);
            while (true) {
                final Socket socket = ss.accept();//阻塞
                new Thread(){
                    public void run(){
                        try{
                              BufferedReader in = new BufferedReader(new InputStreamReader(
                                                  socket.getInputStream()));
                              String msg = null;
                              while((msg = in.readLine()) != null){//readLine阻塞方法
                                     System.out.println(msg);
                              }
                        }catch(Exception e){
                                 e.printStackTrace();
                       } finally{
                             if(socket != null){
                                  socket.close();
                             }
                         }
                    }
                }.start();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
}
 这种方式的IO操作模式虽然解决了服务端为单线程的缺陷,但是还是有很多缺陷:
  1. 当客户端较多时,会创建大量的处理线程。且每个线程都要占用栈空间和一些CPU时间,造成对服务器的压力。服务端的线程个数和客户端并发访问数呈1:1的正比关系,线程数量快速膨胀后,系统的性能将急剧下降,随着访问量的继续增大,系统最终就死-掉-了
  2. 大量的线程如果需要访问服务端的某些竞争资源,势必需要进行同步操作。每个线程遇到外部未准备好的时候,都会阻塞掉,阻塞的结果就是会带来大量的进程上下文切换。且大部分进程上下文切换可能是无意义的。比如假设一个线程监听某一个端口,一天只会有几次请求进来,但是该 cpu 不得不为该线程不断做上下文切换尝试,大部分的切换以阻塞告终。

阻塞IO + 每个请求被线程池处理

实现很简单,我们只需要将新建线程的地方,交给线程池管理即可。

 

public static void main(String[] args) {
        try {
            ExecutorService executorService = Executors.newFixedThreadPool(60);
            ServerSocket ss = new ServerSocket(10000);
            while (true) {
                final Socket socket = ss.accept();//阻塞
                executorService.execute(new Runnable(){
                     public void run(){
                        try{
                              BufferedReader in = new BufferedReader(new InputStreamReader(
                                                  socket.getInputStream()));
                              String msg = null;
                              while((msg = in.readLine()) != null){//readLine阻塞方法
                                     System.out.println(msg);
                              }
                        }catch(Exception e){
                            e.printStackTrace();
                       } finally{
                             if(socket != null){
                                  socket.close();
                             }
                         }
                    }
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
}
 这种方式实现1个或多个线程处理N个客户端的请求,但是底层还是使用的同步阻塞I/O。

 

 使用FixedThreadPool我们就有效的控制了线程的最大数量,保证了系统有限的资源的控制,但是,正因为限制了线程数量,如果发生大量并发请求,超过最大数量的线程就只能等待,直到线程池中的有空闲的线程可以被复用。而对Socket的输入流进行读取时,会一直阻塞,直到发生:

  1.     有数据可读
  2.     可用数据以及读取完毕
  3.     发生空指针或I/O异常
所以在读取数据较慢时(比如数据量大、网络传输慢等),大量并发的情况下,其他接入的消息,只能一直等待,这就是最大的弊端。
分享到:
评论

相关推荐

    java-IO框架图

    Java NIO(New IO)是Java 1.4引入的新特性,是对传统IO的一个补充。NIO提供了非阻塞的IO操作,通过Channel和Buffer对象实现。通道(Channel)类似于流,但它们是双向的,可以同时用于读写。缓冲区(Buffer)是用来...

    高级IO_高级IO思维导图_

    - Java传统IO基于流,分为字节流(InputStream/OutputStream)和字符流(Reader/Writer),以及它们的缓冲流和转换流。 - 流是单向的,数据只能从源到目标进行传输,无法同时进行读写操作。 - 传统IO基于阻塞I/O...

    java.io包详细解说

    此外,Java NIO(New IO)在Java 1.4引入,是对传统IO的补充,提供了非阻塞IO、选择器和通道等高级特性。NIO的设计也遵循了类似的原则,虽然不是严格意义上的Decorator模式,但同样可以动态组合不同的通道和缓冲区,...

    java_IO操作

    - Java NIO(New I/O)提供了与传统IO不同的I/O模型,允许非阻塞读写操作,提高了I/O性能,特别是在高并发场景下。 以上就是Java IO操作的基本知识,包括了读写文件的各种方式以及相关的文件操作。在实际开发中,...

    java io 与java nio区别

    与传统IO相比,NIO的最大特点是支持非阻塞模式,即在进行读写操作时,线程可以选择不等待操作完成而去做其他事情。这极大地提高了并发性能,尤其是在处理大量连接请求时。 ### Java IO 与 Java NIO 的具体区别 ###...

    Java IO应届生培训讲义

    Java BIO即传统的Java IO,采用同步阻塞模式,适用于连接数量不多且固定的场景。BIO中的流是阻塞的,即当一个线程调用read()或write()时,该线程被阻塞,直到有一些数据被读取或写入,该线程才能继续执行。 7. Java...

    Java IO

    Java NIO提供了与传统I/O不同的I/O工作方式,它引入了缓冲区(Buffer)和通道(Channel)的概念,支持非阻塞式IO。相对于传统的IO,NIO支持面向缓冲区的、基于通道的I/O操作。NIO还引入了选择器(Selector)的概念,允许...

    Java IO_NIO

    然而,传统的IO模型在处理大量并发连接时表现出效率较低的问题,为了解决这个问题,Java引入了NIO(Non-blocking Input/Output)模型。 **Java IO核心概念** Java IO的核心类包括InputStream、OutputStream、Reader...

    JavaIO大部分类

    - `Channel` 类似于传统IO的流,但可以同时进行读写操作,并且可连接到多个数据源或目标。 - `Selector` 允许单线程监控多个通道,当通道准备就绪时,选择器会通知程序员进行相应的操作,提高了并发处理能力。 5....

    java IO流精讲 JAVA IO流实例开发

    虽然NIO流不在传统的IO流框架内,但了解其工作原理对于高级IO操作至关重要。 在实例开发中,我们可能会遇到文件复制、网络数据传输、日志记录等各种场景。例如,我们可以使用FileInputStream和FileOutputStream实现...

    Java.nio 与Java.io比较

    在探讨Java.nio与Java.io之间的比较时,我们首先需要理解这两个包在Java编程语言中的核心作用和它们各自的优势。Java.io和Java.nio是Java中处理输入/输出操作的两个主要框架,它们各自拥有独特的特性和应用场景。 #...

    java基础之IO流

    - **定义**:Channel类似于传统IO中的文件描述符,但它更灵活且支持双向通信。 - **作用**:Channel用于连接源和目的地,负责实际的数据传输。所有数据的读写操作都需要通过Channel和Buffer共同完成。 - **特点**...

    java IO系统与正则表达式

    NIO的核心是Channel和Buffer,Channel类似于传统IO中的流,但可以同时进行读写操作;Buffer用于存储数据,用户需要先将数据写入Buffer,然后通过Channel传输。 正则表达式(Regular Expression,简称regex)是一种...

    Java IO处理类的汇总

    Channels代表连接到数据源或目标的通道,Selector用于多路复用多个通道,Buffers则替代了传统IO中的缓冲区。 过滤流(Filter Stream)是Java IO的一个重要设计模式,它在原有流的基础上增加额外功能,如数据转换、...

    scalable io in java

    Doug Lea在书中探讨了Java的NIO(New IO)框架,它是对传统IO的一个重大改进。NIO引入了非阻塞IO和选择器(Selector),允许一个线程同时管理多个通道(Channel),极大地提高了系统在处理并发IO请求时的效率。通过...

    Java IO合集

    与传统的IO相比,NIO更注重于通道(Channels)和缓冲区(Buffers)的使用,而非流。NIO的主要优点在于其非阻塞特性,可以在等待数据可用时进行其他处理,提高了系统的并发性能。 "Java NIO"这本书可能涵盖了以下...

    JAVA NIO学习网站

    在Java传统IO中,数据的读写都是通过流来完成,而NIO则引入了通道(Channel)和缓冲区(Buffer)的概念,提供了一种非阻塞的I/O操作方式,极大地提高了Java进行并发I/O处理的能力。 首先,我们来看下NIO的核心组件...

    Java_IO完全总结

    综上所述,Java IO系统不仅解决了传统IO系统面临的许多挑战,而且还提供了一个强大而灵活的API,使得开发者能够轻松处理各种复杂的IO需求。通过深入研究Java IO的设计模式和具体实现,我们可以更加有效地利用这一...

    Scalable io in java.doc

    Java中的Scalable IO指的是能够高效处理大量输入输出操作的能力,尤其在面对高并发和大数据量的场景时。在这个主题中,我们主要关注Java的非阻塞I/O(Non-blocking I/O,通常缩写为NIO)框架。NIO自Java 1.4版本引入...

Global site tag (gtag.js) - Google Analytics