I/O问题可以说是现在海量数据时代下 ,I/O大部分web系统的瓶颈。我们要了解的java I/O(后面简称为(IO))
- IO类库的基本结构
- 磁盘IO的工作机制
- 网络IO的工作机制
- NIO的工作方式
- 同步异步、阻塞非阻塞的区别
- IO优化技巧
IO类库的基本结构
字节流InputStream
提供public abstract int read() throws IOException;
public int read(byte b[]) throws IOException
public int read(byte b[], int off, int len) throws IOException3个读取方法
字节流OutputStream
提供public abstract void write(int b) throws IOException
public void write(byte b[]) throws IOException
public void write(byte b[], int off, int len) throws IOException
public void flush() throws IOException 4个输出方法
总结:不细说如何使用,关键流 可以组合使用,然后要么写到磁盘,要么写到网络,其实写网络也是写文件,不过会有特殊处理,就是让操作系统通过 传输到其他地方。
字符流Writer
Writer提供public void write(String str, int off, int len) throws IOException
public Writer append(CharSequence csq) throws IOException
abstract public void flush() throws IOException 等方法
字符流Reader
Writer提供public int read(java.nio.CharBuffer target) throws IOException
abstract public int read(char cbuf[], int off, int len) throws IOException;
public long skip(long n) throws IOException 等方法
总结:不管是网络还是磁盘传输,最小的存储单元都是字节,而不是字符,转换存在着耗时和烦人的编码问题,所以Java提供了字符流,注意无论是字符还是字节流都有共同的方法那就是close()方法
字节和字符的转换:InputStreamReader和OutPutStreamWriter
上图就是字符解码的过程,举例来说:假设你用FileReader去读取文件,FileReader继承了InputStreamReader,实际上就是读取文件流,然后通过StreamDecoder解码成了char,然后大多数情况下是默认的系统字符集Charset,同理字符编码类似,如下图
磁盘IO的工作机制
标准文件访问方式:读取先读高速缓存,若无则取磁盘;写入就是先将用户地址空间数据搬移到系统高速缓存块,然后有操作系统决定何时写入物理磁盘。
直接IO的方式:应用程序直接从磁盘读取数据,经典的就是数据库管理系统,数据库系统明确知道热点数据,会提前加载到内存中。通常是直接io和异步io结合使用。
同步访问件方式:顾名思义就是数据读写都是同步操作,与标准文件访问方式不同,只有数据真正写入到磁盘才会返回给应用程序写入成功标志。通常用于数据安全性较高的场景,也采用硬件定制的方式。
异步访问文件方式:访问数据的线程发出请求后并不是阻塞等待而是继续下面的操作。提高的是应用程序效率而不是访问文件的效率。
内存映射访问方式:查询系统将一块内存区域与磁盘文件关联起来,当需要访问磁盘数据改为访问高速缓存数据,减少从高速缓存加载硬盘数据,又去复制到程序内存的操作。
网络IO的工作方式
下面演示C/S socket udp nio
- 服务端
package ocr.test;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
public static void main(String[] args) throws IOException {
ServerSocket server = new ServerSocket(10086);//创建serversocket 并绑定端口 10086 1024-65535之间,因为0-1024间有许多操作系统重要端口
System.out.println(">>>>>>>>>>>服务器已经启动!");
Socket socket =server.accept();//阻塞进程等待着 客户端连接
InputStream is =socket.getInputStream();//得到输入流
InputStreamReader isr = new InputStreamReader(is); //字符字节转换流
BufferedReader br = new BufferedReader(isr);//得到缓冲流
String msg = "";
while((msg=br.readLine() )!= null){
System.out.println("服务器:已经接收到客户端信息="+msg);
}
OutputStream os=socket.getOutputStream();//
PrintWriter pw = new PrintWriter(os);//
pw.write("欢迎客户端"+socket.getLocalAddress());
pw.flush();//刷新数据流
br.close();
isr.close();
is.close();
socket.close();
server.close();
os.close();
pw.close();
}
}
- 客户端
package ocr.test;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
public class client {
public static void main(String[] args) throws IOException, Exception {
Socket socket = new Socket("localhost", 10086);
System.out.println(">>>>>>>>>>>客户端已经启动!");
OutputStream os = socket.getOutputStream();
OutputStreamWriter osw = new OutputStreamWriter(os);
osw.write("用户User密码123");
osw.flush();
socket.shutdownOutput();
InputStream is = socket.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String msg ="";
while((msg =br.readLine())!=null){
System.out.println("客户端:接收到服务器信息="+msg);
}
br.close();
isr.close();
is.close();
osw.close();
os.close();
socket.close();
}
}
- 测试效果:
- UDP通信
服务端:
package ocr.test;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
public static void main(String[] args) throws IOException {
DatagramSocket socket = new DatagramSocket(10086);
byte[] msg = new byte[1024];
DatagramPacket pocket = new DatagramPacket(msg, msg.length);
socket.receive(pocket);
String msgs = new String(msg, 0, msg.length);
System.out.println("服务器:已经接收到客户端信息="+msgs);
InetAddress address = pocket.getAddress();
int port = pocket.getPort();
byte[] sendmsg = ("欢迎您"+port).getBytes();
DatagramPacket pocket2 = new DatagramPacket(sendmsg, sendmsg.length, address, port);
socket.send(pocket2);
socket.close();
}
}
客户端:
package ocr.test;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
public class client {
public static void main(String[] args) throws IOException, Exception {
//客户端
//1、定义服务器的地址、端口号、数据
InetAddress address =InetAddress.getByName("localhost");
int port =10086;
byte[] data ="用户名:admin;密码:123".getBytes();
//2、创建数据报,包含发送的数据信息
DatagramPacket packet = new DatagramPacket(data,data.length,address,port);
//3、创建DatagramSocket对象
DatagramSocket socket =new DatagramSocket();
//4、向服务器发送数据
socket.send(packet);
//接受服务器端响应数据
//======================================
//1、创建数据报,用于接受服务器端响应数据
byte[] data2 = new byte[1024];
DatagramPacket packet2 = new DatagramPacket(data2,data2.length);
//2、接受服务器响应的数据
socket.receive(packet2);
String raply = new String(data2,0,packet2.getLength());
System.out.println("我是客户端,服务器说:"+raply);
//4、关闭资源
socket.close();
}
}
- java NIO:前面讲到的是BIO,无论是磁盘还是网络,都存在一个致命缺点就是:阻塞。当一个线程阻塞是,其他线程失去了CPU的使用权。于是我们需要nio这种通信方式。
客户端:给出一个方法!
public static void client(){
ByteBuffer buffer = ByteBuffer.allocate(1024);
SocketChannel socketChannel = null;
try
{
socketChannel = SocketChannel.open();
socketChannel.configureBlocking(false);
socketChannel.connect(new InetSocketAddress("localhost",8080));
if(socketChannel.finishConnect())
{
int i=0;
while(true)
{
TimeUnit.SECONDS.sleep(1);
String info = "I'm "+i+++"-th information from client";
buffer.clear();
buffer.put(info.getBytes());
buffer.flip();
while(buffer.hasRemaining()){
System.out.println(buffer);
socketChannel.write(buffer);
}
}
}
}
catch (IOException | InterruptedException e)
{
e.printStackTrace();
}
finally{
try{
if(socketChannel!=null){
socketChannel.close();
}
}catch(IOException e){
e.printStackTrace();
}
}
}
服务端:
package ocr.test;
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.Iterator;
public class ServerConnect
{
private static final int BUF_SIZE=1024;
private static final int PORT = 8080;
private static final int TIMEOUT = 3000;
public static void main(String[] args)
{
selector();
}
public static void handleAccept(SelectionKey key) throws IOException{
ServerSocketChannel ssChannel = (ServerSocketChannel)key.channel();
SocketChannel sc = ssChannel.accept();
sc.configureBlocking(false);
sc.register(key.selector(), SelectionKey.OP_READ,ByteBuffer.allocateDirect(BUF_SIZE));
}
public static void handleRead(SelectionKey key) throws IOException{
SocketChannel sc = (SocketChannel)key.channel();
ByteBuffer buf = (ByteBuffer)key.attachment();
long bytesRead = sc.read(buf);
while(bytesRead>0){
buf.flip();
while(buf.hasRemaining()){
System.out.print((char)buf.get());
}
System.out.println();
buf.clear();
bytesRead = sc.read(buf);
}
if(bytesRead == -1){
sc.close();
}
}
public static void handleWrite(SelectionKey key) throws IOException{
ByteBuffer buf = (ByteBuffer)key.attachment();
buf.flip();
SocketChannel sc = (SocketChannel) key.channel();
while(buf.hasRemaining()){
sc.write(buf);
}
buf.compact();
}
public static void selector() {
Selector selector = null;
ServerSocketChannel ssc = null;
try{
selector = Selector.open();
ssc= ServerSocketChannel.open();
ssc.socket().bind(new InetSocketAddress(PORT));
ssc.configureBlocking(false);
ssc.register(selector, SelectionKey.OP_ACCEPT);
while(true){
if(selector.select(TIMEOUT) == 0){
System.out.println("==");
continue;
}
Iterator<SelectionKey> iter = selector.selectedKeys().iterator();
while(iter.hasNext()){
SelectionKey key = iter.next();
if(key.isAcceptable()){
handleAccept(key);
}
if(key.isReadable()){
handleRead(key);
}
if(key.isWritable() && key.isValid()){
handleWrite(key);
}
if(key.isConnectable()){
System.out.println("isConnectable = true");
}
iter.remove();
}
}
}catch(IOException e){
e.printStackTrace();
}finally{
try{
if(selector!=null){
selector.close();
}
if(ssc!=null){
ssc.close();
}
}catch(IOException e){
e.printStackTrace();
}
}
}
}
nio 更多参考:
[http://www.importnew.com/19816.html]{http://www.importnew.com/19816.html}