Skip to content

Commit 8428db7

Browse files
committed
完善面试题
1 parent 0e07d69 commit 8428db7

11 files changed

+895
-25
lines changed

IO/Java I-O面试题.html

+77-1
Large diffs are not rendered by default.

IO/Java I-O面试题.md

+166-3
Original file line numberDiff line numberDiff line change
@@ -409,14 +409,177 @@ InputStream类型中的装饰模式:
409409

410410
(3)SeqcueneInputStream可以将两个已有的输入流连接起来,形成一个输入流,从而将多个输入流排列构成一个输入流序列。
411411

412+
### NIO和BIO的区别
412413

413-
### 结束
414+
- 阻塞IO:使用简单,但随之而来的问题就是会形成阻塞,需要独立线程配合,而这些线程在大多数时候都是没有进行运算的。Java的BIO使用这种方式,问题带来的问题很明显,一个Socket需要一个独立的线程,因此,会造成线程膨胀。
415+
- 非阻塞IO:采用轮询方式,不会形成线程的阻塞。Java的NIO使用这种方式,对比BIO的优势很明显,可以使用一个线程进行所有Socket的监听(select)。大大减少了线程数。
414416

417+
### Linux IO模型
415418

416-
文章转载自:
417-
http://blog.csdn.net/u012815721/article/details/25279613
419+
linux下有五种常见的IO模型,其中只有一种异步模型,其余皆为同步模型。如图:
420+
![image_1bk88gscilce1i75jisk9r7n92q.png-26kB][1]
421+
422+
### 阻塞IO模型
423+
424+
阻塞IO模型是最常见的IO模型了,对于所有的“慢速设备”(socket、pipe、fifo、terminal)的IO默认的方式都是阻塞的方式。阻塞就是进程放弃cpu,让给其他进程使用cpu。进程阻塞最显著的表现就是进程睡眠了。阻塞的时间通常取决于数据是否到来。
425+
这种方式使用简单,但随之而来的问题就是会形成阻塞,需要独立线程配合,而这些线程在大多数时候都是没有进行运算的。Java的BIO使用这种方式,问题带来的问题很明显,一个Socket需要一个独立的线程,因此,会造成线程膨胀。
426+
427+
![image_1bk88hgjnqr51p359avht71h3n37.png-20kB][2]
428+
429+
### 非阻塞IO模型
430+
431+
非阻塞IO就是设置IO相关的系统调用为non-blocking,随后进行的IO操作无论有没有可用数据都会立即返回,并设置errno为EWOULDBLOCK或者EAGAIN。我们可以通过主动check的方式(polling,轮询)确保IO有效时,随之进行相关的IO操作。当然这种方式看起来就似乎不太靠谱,浪费了太多的CPU时间,用宝贵的CPU时间做轮询太不靠谱儿了。图示:
432+
![image_1bk88i1t59b11d4dsqb112j1suf3k.png-27.6kB][3]
433+
434+
### 多路复用
435+
436+
为了解决阻塞I/O的问题,就有了I/O多路复用模型,多路复用就是用单独的线程(是内核级的, 可以认为是高效的优化的) 来统一等待所有的socket上的数据, 一当某个socket上有数据后, 就启用用户线程(可能是从线程池中取出, 而不是重新生成), copy socket data, 并且处理message.因为网络延迟的原因, 同时在处理socket data的用户线程往往比实际的socket数量要少很多. 所以实际应用中, 大部分是用线程池, 池中thread数量可随socket的高峰和低谷 而动态调整.
418437

438+
多路复用I/O中内核中统一的wait socket data那部分可以理解成是非阻塞, 也可以理解成阻塞. 可以理解成非阻塞 是因为它不是等到socket数据全部到达再处理, 而是有了一部分数据就会调用用户线程来处理, 理解成阻塞, 是因为它和用户空间(Appliction)层的非阻塞socket的不同是: socket中没有数据时, 内核还是wait(阻塞)的, 而用户空间的非阻塞socket没有数据也会返回, 会造成CPU的浪费.
419439

440+
Linux下的select和poll 就是多路复用模式,poll相对select,没有了句柄数的限制,但他们都是在内核层通过轮询socket句柄的方式来实现的, 没有利用更底层的notify机制. 但就算是这样,相对阻塞socket也已经进步了很多很多了! 毕竟用一个内核线程就解决了,阻塞socket中N多线程都在无谓地wait的局面.
420441

442+
> 多路复用I/O 还是让用户层来copy socket data. 这个过程是将内核中的socket buffer copy到用户空间的 buffer. 这有两个问题: 一是多了一次内核空间switch到用户空间的过程, 二是用户空间层不便暴露很低层但很高效的copy方式(比如DMA), 所以如果由内核层来做这个动作, 可以更好地提高效率!
443+
444+
![image_1bk88j5gruddk8pv918ptd9q41.png-32.7kB][4]
445+
446+
### 信号驱动IO模型
447+
448+
所谓信号驱动,就是利用信号机制,安装信号SIGIO的处理函数(进行IO相关操作),通过监控文件描述符,当其就绪时,通知目标进程进行IO操作(signal handler)。
449+
![image_1bk88jplt7hjr51et81a06k524e.png-23.2kB][5]
450+
451+
### 异步IO模型
452+
453+
由于异步IO请求只是写入了缓存,从缓存到硬盘是否成功不可知,因此异步IO相当于把一个IO拆成了两部分,一是发起请求,二是获取处理结果。因此,对应用来说增加了复杂性。但是异步IO的性能是所有很好的,而且异步的思想贯穿了IT系统放放面面。
454+
![image_1bk88kd1vlm7d0f142hvcd1rln4r.png-19.6kB][6]
455+
456+
### epoll
457+
458+
epoll是Java NIO在linux上的默认实现。相关的工具可以关注select,poll,关于三者间的区别,参见这里。在Mac上类似的实现是kqueue,Solaris上是/dev/poll。
459+
epoll的优点
460+
461+
- 支持一个进程打开大数目的socket描述符(FD)
462+
- IO效率不随FD数目增加而线性下降
463+
464+
> 传统的select/poll另一个致命弱点就是当你拥有一个很大的socket集合,不过由于网络延时,任一时间只有部分的socket是”活跃”的,但是select/poll每次调用都会线性扫描全部的集合,导致效率呈现线性下降。但是epoll不存在这个问题,它只会对”活跃”的socket进行操作—这是因为在内核实现中epoll是根据每个fd上面的callback函数实现的。那么,只有”活跃”的socket才会主动的去调用 callback函数,其他idle状态socket则不会,在这点上,epoll实现了一个”伪”AIO,因为这时候推动力在os内核。
465+
466+
- 使用mmap加速内核与用户空间的消息传递。
467+
468+
- 内核微调
469+
470+
epoll有2种工作方式:LT和ET:
471+
472+
- LT(level triggered)是缺省的工作方式,并且同时支持block和no-block socket.在这种做法中,内核告诉你一个文件描述符是否就绪了,然后你可以对这个就绪的fd进行IO操作。如果你不作任何操作,内核还是会继续通知你的,所以,这种模式编程出错误可能性要小一点。传统的select/poll都是这种模型的代表.
473+
- ET (edge-triggered)是高速工作方式,只支持no-block socket。在这种模式下,当描述符从未就绪变为就绪时,内核通过epoll告诉你。然后它会假设你知道文件描述符已经就绪,并且不会再为那个文件描述符发送更多的就绪通知,直到你做了某些操作导致那个文件描述符不再为就绪状态了(比如,你在发送,接收或者接收请求,或者发送接收的数据少于一定量时导致了一个EWOULDBLOCK 错误)。但是请注意,如果一直不对这个fd作IO操作(从而导致它再次变成未就绪),内核不会发送更多的通知(only once),不过在TCP协议中,ET模式的加速效用仍需要更多的benchmark确认。
474+
475+
### Zero Copy
476+
477+
上面多次提到内核空间和用户空间的switch, 在socket read/write这么小的粒度频繁调用, 代价肯定是很大的.
478+
所以可以在网上看到Zero Copy的技术, 说到底Zero Copy的思路就是: 分析你的业务, 看看是否能避免不必要的跨空间copy,比如可以用 sendfile()函数充分利用内核可以调用DMA的优势, 直接在内核空间将文件的内容通过socket发送出去,而不必经过用户空间.显然,sendfile是有很多的前提条件的, 如果你想让文件内容作一些变换再发出去,就必须要经过用户空间的Appliation logic, 也是无法使用sendfile了.还有一种方式就是象epoll所做的,用内存映射. 据我所知,kafka速度快的一个原因就是使用了零拷贝。
479+
关于零拷贝,可以看这篇文章
480+
481+
### C10K 问题
482+
网络服务在处理数以万计的客户端连接时,往往出现效率低下甚至完全瘫痪,这被 称为C10K问题。随着互联网的迅速发展,越来越多的网络服务开始面临 C10K 问题, 作为大型网站的开发人员有必要对C10K问题有一定的了解。
483+
C10K问题的最大特点是:设计不够良好的程序,其性能和连接数及机器性能的关系往往是非线性的。举个例子:如果没有考虑过C10K问题,一个经典的基于select的程序能在旧服务器上很好处理1000并发的吞吐量,它在2倍性能新服务器上往往处理不了并发2000的吞吐量。
484+
这是因为在策略不当时,大量操作的消耗和当前连接数n成线性相关。会导致单个任务的资源消耗和当前连接数的关系会是O(n)。而服务程序需要同时对数以万计的socket进行I/O处理,积累下来的资源消耗会相当可观,这显然会导致系统吞吐量不能 和机器性能匹配。为解决这个问题,必须改变对连接提供服务的策略。
485+
更详细的资料参考:The C10K problem
486+
487+
### java中有几种类型的流?JDK为每种类型的流提供了一些抽象类以供继承,请说出他们分别是哪些类?
488+
字节输入流:InputStream,字节输出流:OutputStream
489+
字符输入流:Reader,字符输出流:Writer
490+
491+
### 什么是java序列化,如何实现java序列化?
492+
Java对象的序列化指将一个java对象写入OI流中,与此对应的是,对象的反序列化则从IO流中恢复该java对象。
493+
如果要让某个对象支持序列化机制,则必须让它的类是可序列化的,为了让某个类是可序列化的,该类必须实现Serializable接口或Externalizable接口
494+
495+
496+
### 解释一下java.io.Serializable接口(面试常考)
497+
类通过实现 Java.io.Serializable 接口以启用其序列化功能。未实现此接口的类将无法使其任何状态序列化或反序列化。
498+
### 读写原始数据,一般采用什么流?(AC )
499+
A InputStream
500+
B DataInputStream
501+
C OutputStream
502+
D BufferedInputStream
503+
### 为了提高读写性能,可以采用什么流?(DF)
504+
A InputStream
505+
B DataInputStream
506+
C BufferedReader
507+
D BufferedInputStream
508+
E OutputStream
509+
F BufferedOutputStream
510+
### 对各种基本数据类型和String类型的读写,采用什么流?( AD)
511+
A DataInputStream
512+
B BufferedReader
513+
C PrintWriter
514+
D DataOutputStream
515+
E ObjectInputStream
516+
F ObjectOutputStream
517+
### 能指定字符编码的I/O流类型是:(BH )
518+
A Reader
519+
B InputStreamReader
520+
C BufferedReader
521+
D Writer
522+
E PrintWriter
523+
F ObjectInputStream
524+
G ObjectOutputStream
525+
H OutputStreamWriter
526+
### File类型中定义了什么方法来判断一个文件是否存在?( D)
527+
A createNewFile
528+
B renameTo
529+
C delete
530+
D exists
531+
### File类型中定义了什么方法来创建一级目录?( C)
532+
A createNewFile
533+
B exists
534+
C mkdirs
535+
D mkdir
536+
### 对文本文件操作用什么I/O流?(AD )
537+
A FileReader
538+
B FileInputStream
539+
C RandomAccessFile
540+
D FileWriter
541+
### 在unix服务器www.openlab.com.cn上提供了基于TCP的时间服务应用,该应用使用port为13。创建连接到此服务器的语句是:(A )
542+
A Socket s = new Socket(“www.openlab.com.cn”, 13);
543+
B Socket s = new Socket(“www.openlab.com.cn:13”);
544+
C Socket s = accept(“www.openlab.com.cn”, 13);
545+
### 创建一个TCP客户程序的顺序是:(DACBE )
546+
A 获得I/O流
547+
B 关闭I/O流
548+
C 对I/O流进行读写操作
549+
D 建立socket
550+
E 关闭socket
551+
### 创建一个TCP服务程序的顺序是:(BCADEGF )
552+
A 创建一个服务线程处理新的连接
553+
B 创建一个服务器socket
554+
C 从服务器socket接受客户连接请求
555+
D 在服务线程中,从socket中获得I/O流
556+
E 对I/O流进行读写操作,完成与客户的交互
557+
F 关闭socket
558+
G 关闭I/O流
559+
### Java UDP编程主要用到的两个类型是:( BD)
560+
A UDPSocket
561+
B DatagramSocket
562+
C UDPPacket
563+
D DatagramPacket
564+
### TCP/IP是一种:( B)
565+
A 标准
566+
B 协议
567+
C 语言
568+
D 算法
569+
570+
571+
572+
### 结束
573+
574+
575+
文章转载自:
576+
http://blog.csdn.net/u012815721/article/details/25279613
421577

422578

579+
[1]: http://static.zybuluo.com/homiss/tq95mnettxwgivyukwwxpph2/image_1bk88gscilce1i75jisk9r7n92q.png
580+
[2]: http://static.zybuluo.com/homiss/l40bwthl5c00zt1mpi632b5v/image_1bk88hgjnqr51p359avht71h3n37.png
581+
[3]: http://static.zybuluo.com/homiss/7suuki09g1ymwsv3e1vlgbxa/image_1bk88i1t59b11d4dsqb112j1suf3k.png
582+
[4]: http://static.zybuluo.com/homiss/yt9wrdd7grujbp1z8qmpw80y/image_1bk88j5gruddk8pv918ptd9q41.png
583+
[5]: http://static.zybuluo.com/homiss/ou5borc0llzhcfggdk8uek6u/image_1bk88jplt7hjr51et81a06k524e.png
584+
[6]: http://static.zybuluo.com/homiss/qz90av9jfzjfrqnu0hi6d6xe/image_1bk88kd1vlm7d0f142hvcd1rln4r.png
585+

0 commit comments

Comments
 (0)