一BIO(同步阻塞)
java的原生io是一种BIO,也就是阻塞io,下面我们将要简单的模仿一下BIO。
public class BIO { public static void main(String[] args) throws Exception{ ServerSocket server = new ServerSocket(1999); while(true) { Socket client = server.accept(); Thread thread = new Thread(new Runnable() { @Override public void run() { handler(client); } }); thread.start(); } } public static void handler(Socket client){ InputStream input = null; try{ input = client.getInputStream(); byte[] bytes = new byte[1024]; input.read(bytes); System.out.println(new String(bytes)); input.close(); }catch(Exception e) { //处理异常 }finally { try { input.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } |
当然,我们只是简单模仿一下BIO,
这里我们清除,BIO是通过客户端来一个请求,服务器端就创造一个线程来处理请求,这样就是请求和线程是一对一关系,
但是要考虑两个问题,一个线程开销,另一个是阻塞问题。
1.线程开销问题
多线程编程并不是开启线程越多越好,线程切换是需要考虑线程上下文切换的消耗,当切换的消耗太大时,就证明线程太多了。
来一个请求就开一个线程,这就会造成问题,当请求很多,一个是服务器线程上下文切换造成cpu资源浪费,第二个是会超出服务器服务能力。
2阻塞问题
在上面这段模仿的BIO代码上有两处是阻塞的,一个 server.accept();,另一个是read方法,这就会造成当前线程阻塞,浪费cpu资源。
1.对BIO改进的方案,BIO开启线程数时根据请求来控制的,可以使用线程池技术来优化,但是效果是不太好的,
2.第二个方案是还是在线程数量上做文章,BIO的线程数时不可控的,但是我们想要把他变成服务器可以控制的,
3.第三个方法是在阻塞的问题上,主要是由于线程阻塞,不能干其他事,这主要是对文件等一些读写操作造成的,那我们能不能利用这样的阻塞时间。
二NIO(同步不阻塞)
个人觉得,NIO是针对改进方案的2,3条来改进,
NIO的工作模式,selector是一个轮询器,当轮询到的channel有需要处理的事件,就处理,处理完了接着轮询。也就会一个selector处理多个channel,这里的channel可以看做是一个socket,也就是一个请求。channel数据是buffer,buffer是一个块,通俗来讲就是数组,可读写。普遍量一个线程只有一个selector,这样我们可以控制开多少个线程,一个selector可以处理多个channel,而且是不阻塞的,这样就可以利用到BIO中线程阻塞的时间。
NIO的三大组件Selector,Channel,Buffer
首先我们来简单了解一下Buffer,buffer有四个重要变量,一个是mark 是一个变量,初始值为-1,一个是position 这是是接下来需要读写的位置,一个capacity,这个是buffer的容量,buffer底层其实就是一个数组,capacity就是数组的大小,最后一个是limit,这个位置是可以读或者写到最末位置。读写的切换必须调用flip方法。
除boolean 外,其他java的基本数据类型都有对应的buffer,比如ByteBuffer,IntBuffer等。