一、几个基本概念

1.同步、异步、阻塞、非阻塞

同步:用户触发IO操作,你发起了请求就得等着对方给你返回结果,你不能走,针对调用方的,你发起了请求你等

异步:触发触发了IO操作,即发起了请求以后可以做自己的事,等处理完以后会给你返回处理完成的标志,针对调用方的,你发起了请求你不等

阻塞:你调用我,我试图对文件进行读写的时候发现没有可读写的文件,我的程序就会进入等待状态,等可以读写了,我处理完给你返回结果,这里的等待和同步的等待有很大的区别,针对服务提供方的,你调用我我发现服务不可用我等

非阻塞:你调用我,我试图对文件读写的时候发现没有读写的文件,不等待直接返回,等我发现可以读写文件处理完了再给你返回成功标志,针对服务提供方的,你调用我我不等,我处理完了给你返回结果

2、Java对BIO、NIO、AIO的支持:
Java BIO : 同步阻塞:你调用我,你等待我给你返回结果,我发现没有可读写的资源我也等待,两个一起等,JDK1.4以前的唯一选择,适用于数目比较少并且比较固定的架构,对服务器资源要求比较高,大家都在等资源,等服务提供方处理完了再给你返回结果
Java NIO :同步非阻塞: 你调用我,你等待我给你返回结果,我发现没有可以读写的资源,我不等待先直接返回,等我发现有可以读写的资源以后处理完给你返回结果,适用于连接数目多且连接时间比较短(轻操作)的架构,比如聊天服务器,并发局限于应用中,编程比较复杂,JDK1.4开始支持。
Java AIO(NIO.2) : 异步非阻塞:你调用我,你不等待继续做自己的事,我发现没有可以读写的资源,我也不等待继续做我自己的事,等有可以读写的资源的时候我处理完给你返回结果,适用于连接数目多且连接时间比较长(重操作)的架构,比如相册服务器,充分调用OS参与并发操作,编程比较复杂,JDK7开始支持。
3、BIO、NIO、AIO适用场景分析:
BIO方式适用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,JDK1.4以前的唯一选择,但程序直观简单易理解。
NIO方式适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,并发局限于应用中,编程比较复杂,JDK1.4开始支持。
AIO方式使用于连接数目多且连接比较长(重操作)的架构,比如相册服务器,充分调用OS参与并发操作,编程比较复杂,JDK7开始支持。
 
另外,I/O属于底层操作,需要操作系统支持,并发也需要操作系统的支持,所以性能方面不同操作系统差异会比较明显。

二、NIO基础

1、传统BIO模型-InputStream、OutputStream

传统BIO是一种同步的阻塞IO,IO在进行读写时,该线程将被阻塞,线程无法进行其它操作。

IO流在读取时,会阻塞。直到发生以下情况:1、有数据可以读取。2、数据读取完成。3、发生异常。

服务端:

BioServer.java

 import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket; public class BioServer {
public static void main(String[] args) {
int port=8080; //服务端默认端口
if(args != null && args.length>0){
try {
port = Integer.valueOf(args[0]);
} catch (NumberFormatException e) {
}
}
ServerSocket server = null;
try {
server = new ServerSocket(port);
System.out.println("启动了服务端,端口:"+port);
Socket socket = null;
while(true){
socket = server.accept();//阻塞等待客户端连接
new Thread(new BioServerHandler(socket)).start();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if(server!=null){
System.out.println("关闭了服务.");
try {
server.close();
server = null;
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}

BioServerHandler.java

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket; public class BioServerHandler implements Runnable { private Socket socket;
public BioServerHandler(Socket socket){
this.socket = socket;
} @Override
public void run() {
BufferedReader in = null;
try {
in = new BufferedReader(new InputStreamReader(this.socket.getInputStream()));
String body = null;
while(true){
body = in.readLine(); //阻塞等待数据可以被读取
if(body == null){
break;
}
System.out.println("服务器接收到指令:"+body);
}
} catch (Exception e) {
if(in != null){
try {
in.close();
in = null;//
} catch (IOException e1) {
e1.printStackTrace();
}
}
if(socket != null){
try {
socket.close();
} catch (IOException e1) {
e1.printStackTrace();
}
this.socket = null;
}
}
} }

客户端:

BioServerClient.java

 import java.io.IOException;
import java.io.PrintWriter;
import java.net.Socket; public class BioServerClient { public static void main(String[] args) {
int port=8080; //服务端默认端口
if(args != null && args.length>0){
try {
port = Integer.valueOf(args[0]);
} catch (NumberFormatException e) {
}
}
Socket socket = null;
PrintWriter out = null;
try {
socket = new Socket("127.0.0.1", port);
out = new PrintWriter(socket.getOutputStream(), true);
out.println("9527");
System.out.println("客户端向服务端发送了指令");
} catch (Exception e) {
e.printStackTrace();
} finally {
if(out !=null){
out.close();
out = null;
}
if(socket != null){
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
socket = null;
}
}
}
}

2、伪异步IO模型

以传统BIO模型为基础,通过线程池的方式维护所有的IO线程,实现相对高效的线程开销及管理。

服务端:

TimeServer.java

 import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class TimeServer {
public static void main(String[] args) {
int port=8080; //服务端默认端口
ServerSocket server = null;
try {
server = new ServerSocket(port);
System.out.println("The time server is start in port:"+port);
Socket socket = null;
//通过线程池的方式维护所有的IO线程,实现相对高效的线程开销及管理
TimeServerHandlerExecutePool singleExecutor = new TimeServerHandlerExecutePool(50, 10000); while(true){
socket = server.accept();
// new Thread(new TimeServerHandler(socket)).start();
singleExecutor.execute(new TimeServerHandler(socket));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if(server!=null){
System.out.println("The time server is close.");
try {
server.close();
server = null;
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}

TimeServerHandler.java

 import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Date; public class TimeServerHandler implements Runnable { private Socket socket;
public TimeServerHandler(Socket socket){
this.socket = socket;
} @Override
public void run() {
BufferedReader in = null;
PrintWriter out = null;
try {
in = new BufferedReader(new InputStreamReader(this.socket.getInputStream()));
out = new PrintWriter(this.socket.getOutputStream(), true);
String currentTime = null;
String body = null;
while(true){
body = in.readLine();
if(body == null){
break;
}
System.out.println("The time server(Thread:"+Thread.currentThread()+") receive order:"+body);
currentTime = "QUERY TIME ORDER".equalsIgnoreCase(body) ? new Date(System.currentTimeMillis()).toString() : "BAD ORDER";
out.println(currentTime);
}
} catch (Exception e) {
if(in != null){
try {
in.close();
in = null;//
} catch (IOException e1) {
e1.printStackTrace();
}
}
if(out != null){
try {
out.close();
out = null;
} catch (Exception e1) {
e1.printStackTrace();
}
}
if(socket != null){
try {
socket.close();
} catch (IOException e1) {
e1.printStackTrace();
}
this.socket = null;
}
}
} }

服务端线程池TimeServerHandlerExecutePool.java

 import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit; public class TimeServerHandlerExecutePool { private ExecutorService executor; public TimeServerHandlerExecutePool(int maxPoolSize, int queueSize) {
executor = new ThreadPoolExecutor(Runtime.getRuntime().availableProcessors(), maxPoolSize, 120l, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(queueSize));
} public void execute(Runnable task) {
executor.execute(task);
} }

客户端:

TimeServerClient.java

 import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket; public class TimeServerClient { public static void main(String[] args) {
int port=8080; //服务端默认端口
Socket socket = null;
BufferedReader in = null;
PrintWriter out = null;
try {
socket = new Socket("127.0.0.1", port);
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
out = new PrintWriter(socket.getOutputStream(), true);
out.println("QUERY TIME ORDER");
System.out.println("Send order to server succeed.");
String resp = in.readLine();
System.out.println("Now is : "+resp);
} catch (Exception e) {
e.printStackTrace();
} finally {
if(out !=null){
out.close();
out = null;
}
if(in != null){
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(socket != null){
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
socket = null;
}
}
}
}

3、NIO模型

NIO(JDK1.4)模型是一种同步非阻塞IO,主要有三大核心部分:Channel(通道),Buffer(缓冲区), Selector(多路复用器)。传统IO基于字节流和字符流进行操作,而NIO基于Channel和Buffer(缓冲区)进行操作,数据总是从通道读取到缓冲区中,或者从缓冲区写入到通道中。Selector(多路复用器)用于监听多个通道的事件(比如:连接打开,数据到达)。因此,单个线程可以监听多个数据通道。

NIO和传统IO(一下简称IO)之间第一个最大的区别是,IO是面向流的,NIO是面向缓冲区的

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

NIO优点:

1、通过Channel注册到Selector上的状态来实现一种客户端与服务端的通信。

2、Channel中数据的读取是通过Buffer , 一种非阻塞的读取方式。

3、Selector 多路复用器  单线程模型,  线程的资源开销相对比较小。

NIO缺点

1. API使用复杂。

2. 需要具备一些多线程编码能力

3. 断线重连问题比较严重

4. NIO还有一些BUG

Channel(通道)

传统IO操作对read()或write()方法的调用,可能会因为没有数据可读/可写而阻塞,直到有数据响应。也就是说读写数据的IO调用,可能会无限期的阻塞等待,效率依赖网络传输的速度。最重要的是在调用一个方法前,无法知道是否会被阻塞。

NIO的Channel抽象了一个重要特征就是可以通过配置它的阻塞行为,来实现非阻塞式的通道。

Channel是一个双向通道,与传统IO操作只允许单向的读写不同的是,NIO的Channel允许在一个通道上进行读和写的操作。

FileChannel:文件

SocketChannel:

ServerSocketChannel:

DatagramChannel: UDP

Buffer(缓冲区)

Bufer顾名思义,它是一个缓冲区,实际上是一个容器,一个连续数组。Channel提供从文件、网络读取数据的渠道,但是读写的数据都必须经过Buffer。

Buffer缓冲区本质上是一块可以写入数据,然后可以从中读取数据的内存。这块内存被包装成NIO Buffer对象,并提供了一组方法,用来方便的访问该模块内存。为了理解Buffer的工作原理,需要熟悉它的三个属性:capacity、position和limit。

属性:capacity、position和limit

position和limit的含义取决于Buffer处在读模式还是写模式。不管Buffer处在什么模式,capacity的含义总是一样的。见下图:

capacity:作为一个内存块,Buffer有固定的大小值,也叫作“capacity”,只能往其中写入capacity个byte、long、char等类型。一旦Buffer满了,需要将其清空(通过读数据或者清楚数据)才能继续写数据。

position:当你写数据到Buffer中时,position表示当前的位置。初始的position值为0,当写入一个字节数据到Buffer中后,position会向前移动到下一个可插入数据的Buffer单元。position最大可为capacity-1。当读取数据时,也是从某个特定位置读,将Buffer从写模式切换到读模式,position会被重置为0。当从Buffer的position处读取一个字节数据后,position向前移动到下一个可读的位置。

limit:在写模式下,Buffer的limit表示你最多能往Buffer里写多少数据。 写模式下,limit等于Buffer的capacity。当切换Buffer到读模式时, limit表示你最多能读到多少数据。因此,当切换Buffer到读模式时,limit会被设置成写模式下的position值。换句话说,你能读到之前写入的所有数据(limit被设置成已写数据的数量,这个值在写模式下就是position)

Buffer的分配:对Buffer对象的操作必须首先进行分配,Buffer提供一个allocate(int capacity)方法分配一个指定字节大小的对象。

向Buffer中写数据:写数据到Buffer中有两种方式:

1、从channel写到Buffer

int bytes = channel.read(buf); //将channel中的数据读取到buf中

2、通过Buffer的put()方法写到Buffer

buf.put(byte); //将数据通过put()方法写入到buf中

flip()方法:将Buffer从写模式切换到读模式,调用flip()方法会将position设置为0,并将limit设置为之前的position的值。

从Buffer中读数据:从Buffer中读数据有两种方式:

1、从Buffer读取数据到Channel

int bytes = channel.write(buf); //将buf中的数据读取到channel中

2、通过Buffer的get()方法读取数据

byte bt = buf.get(); //从buf中读取一个byte

rewind()方法:Buffer.rewind()方法将position设置为0,使得可以重读Buffer中的所有数据,limit保持不变。Buffer中的数据,读取完成后,依然保存在Buffer中,可以重复读取

clear()与compact()方法:一旦读完Buffer中的数据,需要让Buffer准备好再次被写入,可以通过clear()或compact()方法完成。如果调用的是clear()方法,position将被设置为0,limit设置为capacity的值。但是Buffer并未被清空,只是通过这些标记告诉我们可以从哪里开始往Buffer中写入多少数据。如果Buffer中还有一些未读的数据,调用clear()方法将被"遗忘 "。compact()方法将所有未读的数据拷贝到Buffer起始处,然后将position设置到最后一个未读元素的后面,limit属性依然设置为capacity。可以使得Buffer中的未读数据还可以在后续中被使用。

mark()与reset()方法:通过调用Buffer.mark()方法可以标记一个特定的position,之后可以通过调用Buffer.reset()恢复到这个position上。

Selector(多路复用器)

Selector与Channel是相互配合使用的,将Channel注册在Selector上之后,才可以正确的使用Selector,但此时Channel必须为非阻塞模式。Selector可以监听Channel的四种状态(Connect、Accept、Read、Write),当监听到某一Channel的某个状态时,才允许对Channel进行相应的操作。

Connect:某一个客户端连接成功后

Accept:准备好进行连接

Read:可读

Write:可写

4、NIO示例:

服务端:

MultiplexerTimeServer.java

 package com.studyio.demo3;

 import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Date;
import java.util.Iterator;
import java.util.Set; /**
*
* @author lgs
*
*
*/
public class MultiplexerTimeServer implements Runnable { private Selector selector;
private ServerSocketChannel serverChannel;
private volatile boolean stop; public MultiplexerTimeServer(int port) {
try {
//打开服务端的一个通道channel:ServerSocketChannel
serverChannel = ServerSocketChannel.open();
//把服务端的通道设置为非阻塞模式
serverChannel.configureBlocking(false);
//绑定监听的端口地址
serverChannel.socket().bind(new InetSocketAddress(port), 1024);
//创建Selector(多路复用器)线程
selector = Selector.open();
//将服务端通道ServerSocketChannel注册到Selector,交给Selector监听,告诉客户端服务端是可以连接的了
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("The time server is start in port:"+port);
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
} public void stop(){
this.stop = true;
}
@Override
public void run() {
//处理客户端消息
while(!stop){
try {
//通过Selector循环准备就绪的Key,这个key指的是客户端的通道
selector.select();
//拿到key以后把key放入迭代器iterator
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectionKeys.iterator();
SelectionKey selectionKey = null;
while(iterator.hasNext()){
selectionKey = iterator.next();
//取到key以后就移出,避免重复取
iterator.remove();
try {
//处理客户端传递过来的数据
handleInput(selectionKey);
} catch (Exception e) {
if(selectionKey!=null){
selectionKey.cancel();
if(selectionKey.channel()!=null){
selectionKey.channel().close();
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
if(selector !=null){
try {
selector.close();
} catch (IOException e) {
e.printStackTrace();
}
}
} /**
* 处理客户端传递过来的数据
* @param selectionKey
* @throws IOException
*/
private void handleInput(SelectionKey selectionKey) throws IOException {
if(selectionKey.isValid()){
//客户端是可连接的
if (selectionKey.isAcceptable()) {
ServerSocketChannel server = (ServerSocketChannel) selectionKey.channel();
//多路复用器监听到新的客户端连接,处理连接请求,完成TCP三次握手。
SocketChannel client = server.accept();
//设置为非阻塞模式
client.configureBlocking(false);
// 将新连接注册到多路复用器上,监听其读操作,读取客户端发送的消息。
client.register(selector, SelectionKey.OP_READ);
}
//客户端是可读的
if(selectionKey.isReadable()){
//获取取客户端的通道
SocketChannel client = (SocketChannel) selectionKey.channel();
ByteBuffer receivebuffer = ByteBuffer.allocate(1024);
//读取客户端请求消息到缓冲区
int count = client.read(receivebuffer); //非阻塞
if (count > 0) {
receivebuffer.flip();
byte[] bytes = new byte[receivebuffer.remaining()]; //remaining()方法
//从缓冲区读取消息到bytes数组里面
receivebuffer.get(bytes);
String body = new String(bytes, "UTF-8");
System.out.println("The time server(Thread:"+Thread.currentThread()+") receive order : "+body);
//将currentTime响应给客户端(客户端Channel)
String currentTime = "QUERY TIME ORDER".equalsIgnoreCase(body) ? new Date(System.currentTimeMillis()).toString() : "BAD ORDER";
//服务端向客户端响应数据,通过客户端的通道传递数据
doWrite(client, currentTime);
}else if(count < 0){
selectionKey.channel();
client.close();
}else{ }
}
}
} /**
* 服务端向客户端响应数据,通过客户端的通道传递数据
* @param client
* @param currentTime
* @throws IOException
*/
private void doWrite(SocketChannel client, String currentTime) throws IOException {
if(currentTime != null && currentTime.trim().length()>0){
ByteBuffer sendbuffer = ByteBuffer.allocate(1024);
sendbuffer.put(currentTime.getBytes());
sendbuffer.flip();
//将客户端响应消息写入到客户端Channel中。
client.write(sendbuffer);
System.out.println("服务器端向客户端发送数据--:" + currentTime);
}else{
System.out.println("没有数据");
}
} }

服务端入口程序TimeServer.java

public class TimeServer {

    public static void main(String[] args) {
int port=8080; //服务端默认端口
MultiplexerTimeServer timeServer=new MultiplexerTimeServer(port);
new Thread(timeServer, "NIO-MultiplexerTimeServer-001").start();
}
}

 客户端:

TimeClientHandler.java

 package com.studyio.demo3;

 import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set; /**
*
* @author lgs
*
*/
public class TimeClientHandler implements Runnable { private String host;
private int port;
private SocketChannel socketChannel;
private Selector selector;
private volatile boolean stop; public TimeClientHandler(String host, int port) {
this.host = host;
this.port = port;
try {
//客户端打开一个通道SocketChannel
socketChannel = SocketChannel.open();
//创建Selector(多路复用器)线程
selector = Selector.open();
//设置为非阻塞模式
socketChannel.configureBlocking(false);
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
} @Override
public void run() {
try {
//连接服务端并发送数据
doConnect();
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
//处理服务端响应的数据,和服务端处理客户端发送的数据一样
while(!stop){
//轮训通道的状态
try {
selector.select(1000);
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectionKeys.iterator();
SelectionKey selectionKey = null;
while(iterator.hasNext()){
selectionKey = iterator.next();
//取到key以后就移出,避免重复取
iterator.remove();
try {
//处理服务端响应的数据
handleInput(selectionKey);
} catch (Exception e) {
if(selectionKey!=null){
selectionKey.cancel();
if(selectionKey.channel()!=null){
selectionKey.channel().close();
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
}
if(selector !=null){
try {
selector.close();
} catch (IOException e) {
e.printStackTrace();
}
}
} /**
* 处理服务端响应的数据
* @param selectionKey
* @throws Exception
*/
private void handleInput(SelectionKey selectionKey) throws Exception {
if(selectionKey.isValid()){
SocketChannel client = (SocketChannel) selectionKey.channel();
if (selectionKey.isConnectable()){
if(client.finishConnect()){
client.register(selector, SelectionKey.OP_READ);
doWrite(client);
}else{
System.exit(1);
}
}
if (selectionKey.isReadable()) {
ByteBuffer receivebuffer = ByteBuffer.allocate(1024);
int count = client.read(receivebuffer);
if (count > 0) {
receivebuffer.flip();
byte[] bytes = new byte[receivebuffer.remaining()]; //remaining()方法
receivebuffer.get(bytes);
String body = new String(bytes, "UTF-8");
System.out.println("Now is "+body);
this.stop = true;
}else if(count < 0){
selectionKey.channel();
client.close();
}else{ }
}
}
} /**
* 连接服务端并发送数据
* @throws Exception
*/
private void doConnect() throws Exception {
//连接服务端
boolean connect = socketChannel.connect(new InetSocketAddress(host, port));
//判断是否连接成功,如果连接成功,则监听Channel的读状态。
if(connect){
//连接成功就把客户端的通道注册到多路复用器上,并设置通道状态为可读
socketChannel.register(selector, SelectionKey.OP_READ);
//写数据 写给服务端
doWrite(socketChannel);
}else{
//如果没有连接成功,则向多路复用器注册Connect(可连接)状态
socketChannel.register(selector, SelectionKey.OP_CONNECT);
} } /**
* 写数据 写给服务端
* @param channel
* @throws IOException
*/
private void doWrite(SocketChannel channel) throws IOException {
ByteBuffer sendbuffer = ByteBuffer.allocate(1024);
sendbuffer.put("QUERY TIME ORDER".getBytes());
sendbuffer.flip();
//向Channel中写入客户端的请求指令 写到服务端 写到通道里面
channel.write(sendbuffer);
if(!sendbuffer.hasRemaining()){
System.out.println("Send order to server succeed.");
}
}
}

客户端程序入口:TimeServerClient.java

public class TimeServerClient {

    public static void main(String[] args) {
int port=8080; //服务端默认端口
new Thread(new TimeClientHandler("127.0.0.1", port), "NIO-TimeServerClient-001").start();
}
}

BIO、NIO、AIO系列一:NIO的更多相关文章

  1. IO NIO AIO及常用框架概述

    概述 nio 同步: 自己亲自出马持银行卡到银行取钱(使用同步IO时,Java自己处理IO读写). 异步: 委托一小弟拿银行卡到银行取钱,然后给你(使用异步IO时,Java将IO读写委托给OS处理,需 ...

  2. 3. 彤哥说netty系列之Java BIO NIO AIO进化史

    你好,我是彤哥,本篇是netty系列的第三篇. 欢迎来我的公从号彤哥读源码系统地学习源码&架构的知识. 简介 上一章我们介绍了IO的五种模型,实际上Java只支持其中的三种,即BIO/NIO/ ...

  3. I/O模型系列之三:IO通信模型BIO NIO AIO

    一.传统的BIO 网络编程的基本模型是Client/Server模型,也就是两个进程之间进行相互通信,其中服务端提供位置信息(绑定的IP地址和监听端口),客户端通过连接操作向服务端监听的地址发起连接请 ...

  4. (转)也谈BIO | NIO | AIO (Java版)

    原文地址: https://my.oschina.net/bluesky0leon/blog/132361 关于BIO | NIO | AIO的讨论一直存在,有时候也很容易让人混淆,就我的理解,给出一 ...

  5. 网络通信简单实例BIO,NIO,AIO

    这里,我将做一个简单的通信程序,分别使用三种原始的通信工具:BIO,NIO,AIO. 功能就是一个服务器,一个客户端.服务器就是处理请求,返回响应.而客户端就是连接服务器,发送请求,接收响应. 第一步 ...

  6. BIO,NIO,AIO

    同步阻塞IO(JAVA BIO):     同步并阻塞,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可 ...

  7. 拿搬东西来解释udp tcpip bio nio aio aio异步

     [群主]雷欧纳德简单理解 tcpip是有通信确认的面对面通信   有打招呼的过程  有建立通道的过程 有保持通道的确认    有具体传输udp是看到对面的人好像在对面等你 就往对面扔东西[群主]雷欧 ...

  8. 也谈BIO | NIO | AIO (Java版--转)

    关于BIO | NIO | AIO的讨论一直存在,有时候也很容易让人混淆,就我的理解,给出一个解释: BIO | NIO | AIO,本身的描述都是在Java语言的基础上的.而描述IO,我们需要从两个 ...

  9. IO回忆录之怎样过目不忘(BIO/NIO/AIO/Netty)

    有热心的网友加我微信,时不时问我一些技术的或者学习技术的问题.有时候我回微信的时候都是半夜了.但是我很乐意解答他们的问题.因为这些年轻人都是很有上进心的,所以在我心里他们就是很优秀的,我愿意多和努力的 ...

随机推荐

  1. [Windows Azure] How to use the Windows Azure Blob Storage Service in .NET

    How to use the Windows Azure Blob Storage Service in .NET version 1.7 version 2.0 This guide will de ...

  2. 序列化ADODataSet, ADOQuery

    经过昨天晚上一折腾把做了一个DIOCP直接传递TADOQuery的Demo,在google上找了一系列的资料. 这样服务端可以直接将TADOQuery查询出来的数据直接转换成TStream自己进行传递 ...

  3. sql随机查询数据order by newid()

    方法1:最普通的写法,性能慢 ID,name FROM dt_keyword ORDER BY NEWID() 方法2:性能还可以 //先给数据库增加一列ALTER TABLE dt_keyword ...

  4. ss安装

    安装很简单,如下: apt-get install python-pip pip install shadowsocks 配置文件格式如下: { "server":"0. ...

  5. 开源实时日志分析ELK

    开源实时日志分析ELK 2018-01-04 转自:开源实时日志分析ELK平台部署 日志主要包括系统日志.应用程序日志和安全日志.系统运维和开发人员可以通过日志了解服务器软硬件信息.检查配置过程中的错 ...

  6. Oracle使用Sql把XML解析成表(Table)的方法

    SELECT * FROM XMLTABLE('$B/DEAL_BASIC/USER_DEAL_INFO' PASSING XMLTYPE('<?xml version="1.0&qu ...

  7. 【Java】PreparedStatement VS Statement

    创建时: Statement statement = conn.createStatement();    PreparedStatement preStatement = conn.prepareS ...

  8. stopManagedWebLogic.sh强制关闭Managed Server

    Adding force shutdown of managed server in weblogic. ----------------------------------------------- ...

  9. WPF Image控件 Source: Byte[] ,BitmapImage 相互转换

    文件转为byte[] FileStream fs = new FileStream(filepath, FileMode.Open, FileAccess.Read); byte[] desBytes ...

  10. testUrl

    def ConnectTest(url): try: response = requests.get(url) return response.status_code except requests. ...