@@ -409,14 +409,177 @@ InputStream类型中的装饰模式:
409
409
410
410
(3)SeqcueneInputStream可以将两个已有的输入流连接起来,形成一个输入流,从而将多个输入流排列构成一个输入流序列。
411
411
412
+ ### NIO和BIO的区别
412
413
413
- ### 结束
414
+ - 阻塞IO:使用简单,但随之而来的问题就是会形成阻塞,需要独立线程配合,而这些线程在大多数时候都是没有进行运算的。Java的BIO使用这种方式,问题带来的问题很明显,一个Socket需要一个独立的线程,因此,会造成线程膨胀。
415
+ - 非阻塞IO:采用轮询方式,不会形成线程的阻塞。Java的NIO使用这种方式,对比BIO的优势很明显,可以使用一个线程进行所有Socket的监听(select)。大大减少了线程数。
414
416
417
+ ### Linux IO模型
415
418
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的高峰和低谷 而动态调整.
418
437
438
+ 多路复用I/O中内核中统一的wait socket data那部分可以理解成是非阻塞, 也可以理解成阻塞. 可以理解成非阻塞 是因为它不是等到socket数据全部到达再处理, 而是有了一部分数据就会调用用户线程来处理, 理解成阻塞, 是因为它和用户空间(Appliction)层的非阻塞socket的不同是: socket中没有数据时, 内核还是wait(阻塞)的, 而用户空间的非阻塞socket没有数据也会返回, 会造成CPU的浪费.
419
439
440
+ Linux下的select和poll 就是多路复用模式,poll相对select,没有了句柄数的限制,但他们都是在内核层通过轮询socket句柄的方式来实现的, 没有利用更底层的notify机制. 但就算是这样,相对阻塞socket也已经进步了很多很多了! 毕竟用一个内核线程就解决了,阻塞socket中N多线程都在无谓地wait的局面.
420
441
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
421
577
422
578
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