java并法库_java基础-网络编程(Socket)技术选型入门之NIO技术

java基础-网络编程(Socket)技术选型入门之NIO技术

作者:尹正杰

版权声明:原创作品,谢绝转载!否则将追究法律责任。

一.传统的网络编程

1>.编写socket通信的MyServer,使用分线程完成和每个client的通信。

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

1 /*

2 @author :yinzhengjie3 Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/

4 EMAIL:y1053419035@qq.com5 */

6 packagecn.org.yinzhengjie.socket;7

8 importjava.io.OutputStream;9 importjava.net.InetSocketAddress;10 importjava.net.ServerSocket;11 importjava.net.Socket;12

13 public classMyServer {14 public static void main(String[] args) throwsException {15 //服务器套接字

16 ServerSocket ss = new ServerSocket(8888) ;17 while(true){18 //接受连接,

19 System.out.println("正在接受连接.....");20 Socket sock =ss.accept();21 newCommThread(sock).start();22 }23 }24 }25

26 /**

27 * 服务器和每个客户端的通信线程28 */

29 class CommThread extendsThread{30 privateSocket sock;31

32 publicCommThread(Socket sock){33 this.sock =sock ;34 }35

36 public voidrun() {37 try{38 //获取远程地址和端口

39 InetSocketAddress addr =(InetSocketAddress) sock.getRemoteSocketAddress();40 int port =addr.getPort();41 String ip =addr.getAddress().getHostAddress();42 System.out.printf("有人连接进来了!! : %s , %d\r\n", ip, port);43

44 //向客户端发送消息

45 int index = 0;46 OutputStream out =sock.getOutputStream();47 while (true) {48 index ++;49 out.write(("yinzhengjie" + index + "\r\n").getBytes());50 out.flush();51 Thread.sleep(1000);52 }53 } catch(Exception e) {54 e.printStackTrace();55 }56 }57 }

MyServer.java 文件内容

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

1 /*

2 @author :yinzhengjie3 Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/

4 EMAIL:y1053419035@qq.com5 */

6 packagecn.org.yinzhengjie.socket;7

8 importjava.io.BufferedReader;9 importjava.io.InputStream;10 importjava.io.InputStreamReader;11 importjava.net.Socket;12

13 public classMyClient {14 public static void main(String[] args) throwsException {15 Socket s = new Socket("localhost" ,8888) ;16 System.out.println("连接到服务器了!!");17 InputStream in =s.getInputStream();18 BufferedReader br = new BufferedReader(newInputStreamReader(in)) ;19 String line = null;20 //获取服务端发来的消息

21 while((line = br.readLine())!= null){22 System.out.println("收到消息 : " +line);23 }24 }25 }

MyClient.java 文件内容

我们先开启上面的服务端,再开启2个客户端,测试结果如下:

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

正在接受连接.....

正在接受连接.....

有人连接进来了!! : 127.0.0.1 , 52704正在接受连接.....

有人连接进来了!! : 127.0.0.1 , 52710

server端输出的输入戳这里

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

1 连接到服务器了!!

2 收到消息 : yinzhengjie13 收到消息 : yinzhengjie24 收到消息 : yinzhengjie35 收到消息 : yinzhengjie46 收到消息 : yinzhengjie57 收到消息 : yinzhengjie68 收到消息 : yinzhengjie79 收到消息 : yinzhengjie810 收到消息 : yinzhengjie911 收到消息 : yinzhengjie1012 收到消息 : yinzhengjie1113 收到消息 : yinzhengjie1214 收到消息 : yinzhengjie1315 收到消息 : yinzhengjie1416 收到消息 : yinzhengjie1517 收到消息 : yinzhengjie1618 收到消息 : yinzhengjie1719 收到消息 : yinzhengjie1820 收到消息 : yinzhengjie1921 收到消息 : yinzhengjie2022 收到消息 : yinzhengjie2123 收到消息 : yinzhengjie2224 收到消息 : yinzhengjie2325 收到消息 : yinzhengjie2426 收到消息 : yinzhengjie2527 收到消息 : yinzhengjie2628 收到消息 : yinzhengjie2729 收到消息 : yinzhengjie2830 收到消息 : yinzhengjie2931 收到消息 : yinzhengjie3032 收到消息 : yinzhengjie3133 收到消息 : yinzhengjie3234 收到消息 : yinzhengjie3335 收到消息 : yinzhengjie3436 收到消息 : yinzhengjie3537 收到消息 : yinzhengjie36

client1输出的数据戳这里

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

连接到服务器了!!收到消息 : yinzhengjie1

收到消息 : yinzhengjie2

收到消息 : yinzhengjie3

收到消息 : yinzhengjie4

收到消息 : yinzhengjie5

收到消息 : yinzhengjie6

收到消息 : yinzhengjie7

client2输出的数据戳这里

2>.分析传统的套接字存在的缺陷(以上述代码为例)

d8924c47c641c470f80096c0e63dd562.png

综上图所述,服务端存在一个缺陷,当客户端的连接数量过多,main线程就会开启过多的线程去响应,如此长久下去,JVM内存耗尽是迟早的事情,因此上述代码再测试使用连接较少的情况下是可取的,但是在连接过多的情况则不推荐使用,实际上我们能想到这一点,开发JAVA语言的大佬们怎么可能想不到这一点呢?因此这些大佬们早在JDK1.4版本就给出了相应的解决方案,它是本篇博客的重点,即新型IO(简称NIO)。

二.NIO简介

1>.I/O瓶颈出现

java.io流类在使用时需要复制大量的小数据块;在不切换线程上下问的情况下没有办法在多个源之间进行数据的复用;也无法利用操作系统的高性能IO特性,比如内存的file的映射(虚拟内存)。

2>.什么是NIO

在 JDK 1. 4 中 新 加入 了 NIO( New Input/ Output) 类, 引入了一种基于通道和缓冲区的 I/O 方式,它可以使用 Native 函数库直接分配堆外内存,然后通过一个存储在 Java 堆的 DirectByteBuffer 对象作为这块内存的引用进行操作,避免了在 Java 堆和 Native 堆中来回复制数据。

NIO 是一种同步非阻塞的 IO 模型。同步是指线程不断轮询 IO 事件是否就绪,非阻塞是指线程在等待 IO 的时候,可以同时做其他任务。同步的核心就是 Selector,Selector 代替了线程本身轮询 IO 事件,避免了阻塞同时减少了不必要的线程消耗;非阻塞的核心就是通道和缓冲区,当 IO 事件就绪时,可以通过写道缓冲区,保证 IO 的成功,而无需线程阻塞式地等待。

3>.NIO的组成部分

Java NIO (New IO)是JDK1.4后引入的新输入/输出API,提供基于缓冲区(buffer)的块写入/读取,而以前的I/O是基于流(Stream)的方式,NIO基于块的IO操作,将最耗时的缓存区读取和填充交由底层操作系统实现,因此速度上要快得多。NIO主要由以下三个核心部分组成:

第一部分:Buffer

答:为什么说NIO是基于缓冲区的IO方式呢?因为,当一个链接建立完成后,IO的数据未必会马上到达,为了当数据到达时能够正确完成IO操作,在BIO(阻塞IO)中,等待IO的线程必须被阻塞,以全天候地执行IO操作。为了解决这种IO方式低效的问题,引入了缓冲区的概念,当数据到达时,可以预先被写入缓冲区,再由缓冲区交给线程,因此线程无需阻塞地等待!

关于Buffer的相关概念如下:1>.capacity

答:容量,即数组的长度。2>.limit

答:限制,可以使用的空间大小。写入完成后,各种操作都是在position和limit之间操作数据。3>.position

答:位置,指针的位置,开始操纵地方,它会随着读写操作自动更新。Position类似于File的指针。4>.mark

答:记号,可以通过reset方法将指针重置到mark的位置上。5>.flip

答:拍板操作 ,设置 limit为当前位置值并将position设置为0,以备用于读取。即limit= position , post = 0;

他们按照大小的排列顺序如下:0 <= mark <= position <= limit <= capacity

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

1 /*

2 @author :yinzhengjie3 Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/

4 EMAIL:y1053419035@qq.com5 */

6 packagecn.org.yinzhengjie.nio;7

8 importjava.nio.ByteBuffer;9

10 public classTestNIO {11 public static voidmain(String[] args) {12 mark();13 }14

15 public static voidmark(){16 //定义一个字节数组

17 byte[] bytes = {'y','i','n','z','h','e','n','g','j','i','e'};18 //将字节数组包装成缓冲区

19 ByteBuffer buf =ByteBuffer.wrap(bytes);20 //修改缓冲区的指针位置为2

21 buf.position(2);22 //对当前指针做标记,即标记当前指针为2

23 buf.mark();24 //在做完标记后,修改指针位置为8

25 buf.position(8);26 //重置指针,这样它会回到上一个mar标记的位置!如果没有配置mark,默认回到0这个指针位置

27 buf.reset();28 //打印当前指针位置

29 System.out.println("当前指针位置是:"+buf.position());30 }31 }32

33 /*

34 以上代码执行结果如下:35 当前指针位置是:236 */

测试mark

第二部分:Channel

答:JAVA中的NIO是通过channel的API进行操控的,通道是 I/O 传输发生时通过的入口,而缓冲区是这些数 据传输的来源或目标。对于离开缓冲区的传输,您想传递出去的数据被置于一个缓冲区,被传送到通道。对于传回缓冲区的传输,一个通道将数据放置在您所提供的缓冲区中。换句话说,channel是到硬件设备、文件、socket或网络组件的打开的连接,这些组件都可以执行IO操作。通道可以是open或closed状态,创建时打开,close后一直保持closed状态,可以通过isOpen判断通道状态。

第三部分:Selector

答:通道和缓冲区的机制,使得线程无需阻塞地等待IO事件的就绪,但是总是要有人来监管这些IO事件。这个工作就交给了selector来完成,这就是所谓的同步。Selector允许单线程处理多个 Channel。如果你的应用打开了多个连接(通道),但每个连接的流量都很低,使用Selector就会很方便。要使用Selector,得向Selector注册Channel,然后调用它的select()方法。这个方法会一直阻塞到某个注册的通道有事件就绪,这就是所说的轮询。一旦这个方法返回,线程就可以处理这些事件。

4>.NIO的变化

第一:BUffer类允许在JVM和OS之间使用很小的内存复制技术来移动数据,不需要过度开销,比如像切换字节的顺序;

第二:统一的Channel类族允许在Buffer,文件或Socket之间直接交换数据而不需要想原有的Stream类那样借助于中间媒介;

第三:NIO首次提供了文件锁定机制;

三.Java NIO和IO的主要区别

java NIO和传统IO还是有区别的,大致分为以下三点:

1>.面向流与面向缓冲

Java NIO和IO之间第一个最大的区别是,IO是面向流的,NIO是面向缓冲区的。

Java IO面向流意味着每次从流中读一个或多个字节,直至读取所有字节,它们没有被缓存在任何地方。此外,它不能前后移动流中的数据。如果需要前后移动从流中读取的数据,需要先将它缓存到一个缓冲区。

Java NIO的缓冲导向方法略有不同。数据读取到一个它稍后处理的缓冲区,需要时可在缓冲区中前后移动。这就增加了处理过程中的灵活性。但是,还需要检查是否该缓冲区中包含所有您需要处理的数据。而且,需确保当更多的数据读入缓冲区时,不要覆盖缓冲区里尚未处理的数据。

2>.阻塞与非阻塞IO

Java IO的各种流是阻塞的。这意味着,当一个线程调用read() 或 write()时,该线程被阻塞,直到有一些数据被读取,或数据完全写入。该线程在此期间不能再干任何事情了。

Java NIO的非阻塞模式,使一个线程从某通道发送请求读取数据,但是它仅能得到目前可用的数据,如果目前没有数据可用时,就什么都不会获取。而不是保持线程阻塞,所以直至数据变的可以读取之前,该线程可以继续做其他的事情。 非阻塞写也是如此。一个线程请求写入一些数据到某通道,但不需要等待它完全写入,这个线程同时可以去做别的事情。 线程通常将非阻塞IO的空闲时间用于在其它通道上执行IO操作,所以一个单独的线程现在可以管理多个输入和输出通道(channel)。

3>.选择器(Selectors)

Java NIO的选择器允许一个单独的线程来监视多个输入通道,你可以注册多个通道使用一个选择器,然后使用一个单独的线程来“选择”通道:这些通道里已经有可以处理的输入,或者选择已准备写入的通道。这种选择机制,使得一个单独的线程很容易来管理多个通道。关于选择器画了一个草图如下:

eca89846bebd5ffd3491873bc2eb6097.png

四.NIO网络编程

1>.NIO实现文件复制

使用NIO技术实现文件复制,核心就是FileChannel + ByteBuffer完成文件的读写。

/*@author :yinzhengjie

Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/EMAIL:y1053419035@qq.com*/

packagecn.org.yinzhengjie.nio;importjava.io.FileInputStream;importjava.io.FileOutputStream;importjava.nio.ByteBuffer;importjava.nio.channels.FileChannel;public classFileDemo {public static void main(String[] args) throwsException {//文件输出

FileInputStream fis = new FileInputStream("D:\\BigData\\JavaSE\\yinzhengjieData\\1.txt");//源文件通道,由于我们的fis对象是文件输入流,因此获取到的srcFC通道也只能进行读取操作!

FileChannel srcFC =fis.getChannel();//输出流

FileOutputStream fos = new FileOutputStream("D:\\BigData\\JavaSE\\yinzhengjieData\\2.txt") ;//输出文件通道,由于我们的fos对象是文件输出流,因此获取到的destFC通道也只能进行写入操作!

FileChannel destFC =fos.getChannel();//分配NIO的4个字节缓冲区,在堆区分配内存。可通过jvisualvm工具查看。

ByteBuffer buf = ByteBuffer.allocate(4) ;//拷贝数据

while(srcFC.read(buf) != -1){//进行拍板操作,即limit为当前position的位置,position回到起始位置

buf.flip();//进行上述操作后,就可以把读取到的数据写入到文件中

destFC.write(buf) ;//重新启用整个缓冲区,即position回到起始位置,limit为capacity的容量!注意:如果这里不clear的话,文件又没有读取完毕,就会陷入死循环状态!

buf.clear();

}

}

}

2>.优化“编写socket通信的MyServer,使用分线程完成和每个client的通信。”

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

1 /*

2 @author :yinzhengjie3 Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/

4 EMAIL:y1053419035@qq.com5 */

6 packagecn.org.yinzhengjie.nio.socket;7

8 importjava.io.ByteArrayOutputStream;9 importjava.net.InetSocketAddress;10 importjava.net.SocketAddress;11 importjava.nio.ByteBuffer;12 import java.nio.channels.*;13 importjava.util.Iterator;14 importjava.util.Set;15

16 public classMyNioServer {17 public static void main(String[] args) throwsException {18 //开启ServerSocket通道

19 ServerSocketChannel ssc =ServerSocketChannel.open();20 //指定服务端地址

21 SocketAddress addr = new InetSocketAddress("www.yinzhengjie.org.cn", 8888);22 //绑定地址

23 ssc.bind(addr);24 //配置非阻塞模式

25 ssc.configureBlocking(false) ;26 //打开挑选器,我们可以通过“selector.keys()”获取注册集,我们还可以通过“selector.selectedKeys()”获取挑选集。没有取消集合,如果想要取消某个key,可以调用它的取消方法(key.cancel();)。

27 Selector selector =Selector.open();28 //注册通道并绑定事件,由于我们是服务端,只需要绑定ACCEPT事件即可!这样我们就可以直接才能够ssc对象里面取得我们感兴趣的事件啦!

29 ssc.register(selector, SelectionKey.OP_ACCEPT) ;30

31 System.out.println("服务端已经开启,等待接收连接中......");32 while(true){33 //开始挑选

34 selector.select();35 //取得挑选出来的key

36 Set keys =selector.selectedKeys();37 //得到迭代器,这是selector挑选出来的key,这些key代表着不同的事件!

38 Iterator it =keys.iterator();39 //判断迭代器是否含有下一个key,然后在里面判断每一个key属于哪个类型?比如可读?可写?可连接?

40 while(it.hasNext()){41 //从迭代器中随机取出一个key!

42 SelectionKey key =it.next() ;43 //判断是否发生接受事件

44 if(key.isAcceptable()){45 //处理接受过程,“key.channel()”里面存放的就是服务端感兴趣的keys(返回的是SelectableChannel类型),而我们只绑定ACCEPT时间,因此里面存放的应该只有accept()事件哟!因此我们可以直接从ssc中取得相应的accept()事件也是可行的!

46 SocketChannel sc =ssc.accept();47 //得到远程的地址的端口

48 InetSocketAddress remoteAddr =(InetSocketAddress)sc.getRemoteAddress();49 String ip =remoteAddr.getAddress().getHostAddress();50 int port =remoteAddr.getPort() ;51 //配置非阻塞,如果想要设置为阻塞模式,我们将里面的值设置为true即可!不推荐!因为非阻塞模式可以轻松实现并发操作!

52 sc.configureBlocking(false) ;53 //注册sc,因为我们的sc是新产生的对象,它也需要向挑选器中注册自己感兴趣的事件!我们这里对读,写,连接事件都感兴趣,他们直接用“|”运算进行连接。

54 sc.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE |SelectionKey.OP_CONNECT) ;55 System.out.printf("%s : %d 连接进来了\r\n", ip , port);56 }57 //读取发来的消息

58 if(key.isReadable()){59 //拿到可读key的通道

60 SocketChannel sc00 =(SocketChannel) key.channel();61 //调用我们定义好的方法读取数据

62 String str =readStrFromChannel(sc00) ;63 System.out.println(sc00 + " 说: " +str);64 }65 //可写

66 if(key.isWritable()){67 SocketChannel sc00 =(SocketChannel) key.channel();68 writeStrToChannel(sc00 , "hi ,I'm Yinzhengjie !!!");69 Thread.sleep(1000);70 }71 //完成连接

72 if(key.isConnectable()){73 SocketChannel sc00 =(SocketChannel) key.channel();74 sc00.finishConnect() ;75 }76 //删除当前key,如果不删除,下次挑选出来还会再次处理!注意,“key.cancel();”是取消当前绑定的key事件,一旦执行,你们在注册集合中注册的key也将不复存在!

77 it.remove();78 }79 }80 }81

82 /**

83 * 从通道中读取文本84 */

85 public staticString readStrFromChannel(SocketChannel sc){86 //定义一个内存字节输出流对象,用于存取读取到的数据。

87 ByteArrayOutputStream baos = newByteArrayOutputStream() ;88 try{89 //分配NIO的1024个字节缓冲区,在堆区分配内存。可通过jvisualvm工具查看。

90 ByteBuffer buf = ByteBuffer.allocate(1024) ;91 int len = -1;92 //读完数据的判定条件是当通道中无数据时,通道的长度为0。

93 while((len = sc.read(buf)) != 0){94 //将读取到的数据写入到内存流中,buf.array()是拿到后台每次读取的缓冲区的数组。

95 baos.write(buf.array(), 0,len) ;96 //读完之后将清空,注意,这里的情况不是清空数组的数据哟!而是将position的值改为0,而limit的位置不变!

97 buf.clear() ;98 }99 return newString(baos.toByteArray()) ;100 } catch(Exception e) {101 e.printStackTrace();102 }103 return "";104 }105

106 /**

107 * 写入消息到通道108 */

109 public static voidwriteStrToChannel(SocketChannel sc , String str){110 try{111 //通过数组包装缓冲区。换句话说,包装数组成为缓冲区。

112 ByteBuffer buf =ByteBuffer.wrap(str.getBytes());113 //将缓冲区数据写入到sc对象中

114 sc.write(buf) ;115 } catch(Exception e) {116 e.printStackTrace();117 }118 }119 }

MyNioServer.java 文件内容

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

1 /*

2 @author :yinzhengjie3 Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/

4 EMAIL:y1053419035@qq.com5 */

6 packagecn.org.yinzhengjie.nio.socket;7

8 importjava.net.InetSocketAddress;9 importjava.net.SocketAddress;10 importjava.nio.channels.SelectionKey;11 importjava.nio.channels.Selector;12 importjava.nio.channels.SocketChannel;13 importjava.util.Set;14

15 public classMyNioClient {16 public static void main(String[] args) throwsException {17 //指定服务端地址

18 SocketAddress serverAddr = new InetSocketAddress("www.yinzhengjie.org.cn" , 8888) ;19 //和服务端建立连接

20 SocketChannel sc =SocketChannel.open(serverAddr) ;21 //配置非阻塞模式

22 sc.configureBlocking(false) ;23 //打开挑选器

24 Selector sel =Selector.open() ;25 //注册通道,得到注册key,由于客户端我们只需要绑定读和写的操作就好!

26 SelectionKey key = sc.register(sel , SelectionKey.OP_READ |SelectionKey.OP_WRITE) ;27

28 int index = 0;29 while(true){30 //开始挑选,返回值n表示已经挑选出来客户单看兴趣的事件啦!

31 int n =sel.select() ;32 //如果n大于0,说明挑选器已经挑选到key事件了,接下来我们就需要完成挑选的类型的判断

33 if(n > 0){34 //获取挑选器挑选出来key的集合

35 Set keys =sel.selectedKeys();36 //判断这个key是否可读,如果可读我们就将数据读取出来!

37 if(key.isReadable()){38 String str =MyNioServer.readStrFromChannel(sc) ;39 System.out.println("收到消息 : " +str);40 }41 //判断这个key是否可写,如果可写我们就往这个key中写入数据。为了试验效果更佳,我们让每次写入时睡眠一秒钟!

42 if(key.isWritable()){43 MyNioServer.writeStrToChannel(sc , "hello" + (index ++));44 Thread.sleep(1000);45 }46 //注意,处理完key的事件后,记得将keys进行清空(删除当前已经处理的keys)操作!否则数据接收可能会收到影响!

47 keys.clear();48 }49 }50 }51 }

MyNioClient.java 文件内容

我们先开启上面的服务端,再开启客户端,测试结果如下:

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

服务端已经开启,等待接收连接中......127.0.0.1 : 64763连接进来了

java.nio.channels.SocketChannel[connected local=/127.0.0.1:8888 remote=/127.0.0.1:64763] 说: hello0

java.nio.channels.SocketChannel[connected local=/127.0.0.1:8888 remote=/127.0.0.1:64763] 说: hello1

java.nio.channels.SocketChannel[connected local=/127.0.0.1:8888 remote=/127.0.0.1:64763] 说: hello2

java.nio.channels.SocketChannel[connected local=/127.0.0.1:8888 remote=/127.0.0.1:64763] 说: hello3

java.nio.channels.SocketChannel[connected local=/127.0.0.1:8888 remote=/127.0.0.1:64763] 说: hello4

java.nio.channels.SocketChannel[connected local=/127.0.0.1:8888 remote=/127.0.0.1:64763] 说: hello5

java.nio.channels.SocketChannel[connected local=/127.0.0.1:8888 remote=/127.0.0.1:64763] 说: hello6

java.nio.channels.SocketChannel[connected local=/127.0.0.1:8888 remote=/127.0.0.1:64763] 说: hello7

java.nio.channels.SocketChannel[connected local=/127.0.0.1:8888 remote=/127.0.0.1:64763] 说: hello8

java.nio.channels.SocketChannel[connected local=/127.0.0.1:8888 remote=/127.0.0.1:64763] 说: hello9

java.nio.channels.SocketChannel[connected local=/127.0.0.1:8888 remote=/127.0.0.1:64763] 说: hello10

java.nio.channels.SocketChannel[connected local=/127.0.0.1:8888 remote=/127.0.0.1:64763] 说: hello11

java.nio.channels.SocketChannel[connected local=/127.0.0.1:8888 remote=/127.0.0.1:64763] 说: hello12

服务端接收数据戳这里

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

收到消息 : hi ,I'm Yinzhengjie !!!

收到消息 : hi ,I'm Yinzhengjie !!!

收到消息 : hi ,I'm Yinzhengjie !!!

收到消息 : hi ,I'm Yinzhengjie !!!

收到消息 : hi ,I'm Yinzhengjie !!!

收到消息 : hi ,I'm Yinzhengjie !!!

收到消息 : hi ,I'm Yinzhengjie !!!

收到消息 : hi ,I'm Yinzhengjie !!!

收到消息 : hi ,I'm Yinzhengjie !!!

收到消息 : hi ,I'm Yinzhengjie !!!

收到消息 : hi ,I'm Yinzhengjie !!!

收到消息 : hi ,I'm Yinzhengjie !!!

收到消息 : hi ,I'm Yinzhengjie !!!

收到消息 : hi ,I'm Yinzhengjie !!!

收到消息 : hi ,I'm Yinzhengjie !!!

客户端接收数据戳这里

3>.单个线程的NIO存在的缺陷

答:我们知道传统的NIO解决并发的问题是需要不断的开启线程实现并发操作,这样导致一个不好的结果无法接受大量并发的连接,因为当连接数过大时,会开启过多的新线程去响应每一个新连接的线程。当开启的新线程数量过多时,服务端需要进行多个线程的上线文切换,从而降低了执行效率,与此同时,还会占用过多的资源消耗!而我们通过NIO技术优化后,只需要开启一个线程就能相应多个客户端的连接,但是这也产生了一个新的问题,由于NIO在处理并发连接时,是通用轮询选择器的方式去相应每一个客户端,当相应某个客户端连接执行的时间过长,这回导致这个执行其它线程时间过长。换句话说,每次处理客户端连接在服务端都是在一个线程里按序执行,如果某个任务它比较占时间那么就会拖慢整个服务器的执行效率!

因此为了解决不断创建多个线程或者只启用一个线程的存在的问题,就出现了一种池化技术的解决方案!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值