这次讲的是JAVA NIO中的大boss:Channel。同样,我只挑重要的类和方法来讲。
Channel 用于在字节缓冲区和位于通道另一侧的实体(通常是一个文件或套接字)之间有效地传输数据,它能大大提高读写效率,是NIO中的重大突破。也是广大java开发者必须要掌握的
I/O 可以分为广义的两大类别:File I/O 和 Stream I/O。那么相应地有两种类型的通道也就不足为怪了,它们是文件( file)通道和套接字( socket)通道。
有一个 FileChannel 类和三个 socket 通道类: SocketChannel、 ServerSocketChannel 和 DatagramChannel。这篇博文只要讲一些基础,而关于两种通道类的具体实现我们将放在进阶篇中做详细讲解
接下来我们直接上代码,根据代码进行讲解。
获取通道类
SocketChannel sc = SocketChannel.open( );
sc.connect (new InetSocketAddress ("somehost", someport));
ServerSocketChannel ssc = ServerSocketChannel.open( );
ssc.socket( ).bind (new InetSocketAddress (somelocalport));
DatagramChannel dc = DatagramChannel.open( );
RandomAccessFile raf = new RandomAccessFile ("somefile", "r");
FileChannel fc = raf.getChannel( );
以上代码是刚刚提到的四种通道类的创建方式,需要注意的是创建socket通道时,socket类同样有一个getChannel方法来返回SocketChannel,但是该方法的作用是在已经存在SocketChannel时返回该SocketChannel,而不能新建一个SocketChannel。要想新建一个SocketChannel,你只能通过上述的SocketChannel的open方法。而FileChannel 则不同,它可以通过file的getChannel方法新建。
另外,如果想通过I/O流去获取通道,则用Channels的静态方法newChannel即可
操作通道类
操作通道类的方法主要是write和read,针对不同通道类读写缓冲区。这里要注意的是区分你要操作的通道类的类型是只读还是读写,举个例子你如果通过FileInputStream获取了一个FileChannel,那么这个FileChannel就是只读的,这时候你如果执行write方法是会报错的。
上代码:
package com.java.cheney.nio;
import java.nio.ByteBuffer;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.nio.channels.Channels;
import java.io.IOException;
/**
*
* Project: javaDemo
* Class: ChannelCopy
* Description:通过通道进行复制
*
* @author : Cheney
* @Date 2016年5月22日 上午10:32:02
* @version 1.0
*
*/
public class ChannelCopy {
public static void main(String[] argv) throws IOException {
ReadableByteChannel source = Channels.newChannel(System.in);
WritableByteChannel dest = Channels.newChannel(System.out);
channelCopy1(source, dest);
//或者替换成channelCopy2(source, dest);
source.close();
dest.close();
}
private static void channelCopy1(ReadableByteChannel src,
WritableByteChannel dest) throws IOException {
//为缓冲区分配大小
ByteBuffer buffer = ByteBuffer.allocateDirect(16 * 1024);
//读取缓冲区直到结束
while (src.read(buffer) != -1) {
buffer.flip();
// 通过通道将dest中的内容写入缓冲区
dest.write(buffer);
//压缩缓冲区,进行下一次循环
buffer.compact();
}
buffer.flip();
//检测缓冲区是否清空
while (buffer.hasRemaining()) {
dest.write(buffer);
}
}
private static void channelCopy2(ReadableByteChannel src,
WritableByteChannel dest) throws IOException {
ByteBuffer buffer = ByteBuffer.allocateDirect(16 * 1024);
//读取缓冲区直到结束
while (src.read(buffer) != -1) {
buffer.flip();
while (buffer.hasRemaining()) {
dest.write(buffer);
}
//清空缓冲区
buffer.clear();
}
}
}
这个程序的功能是你可以通过控制台输入数据到ReadableByteChannel后又被复制到WritableByteChannel后输出,基本的读写操作都在上面包含了~
Scatter/Gather
它是指在多个缓冲区上实现一个简单的 I/O 操作。对于一个 write 操作而言,数据是从几个缓冲区按顺序抽取(称为 gather)并沿着通道发送的。对于 read 操作而言,从通道读取的数据会按顺序被散布(称为 scatter)到多个缓冲区。这一重大的突破目的在于提升读写的性能
上代码:
package com.java.cheney.nio;
import java.nio.ByteBuffer;
import java.nio.channels.GatheringByteChannel;
import java.io.FileOutputStream;
import java.util.Random;
import java.util.List;
import java.util.LinkedList;
/**
*
* Project: javaDemo Class: Marketing Description:
*
* @author : Cheney
* @Date 2016年5月22日 上午11:02:05
* @version 1.0
*
*/
public class Marketing {
private static final String outputFile = "blahblah.txt";
// "Leverage frictionless methodologies"
public static void main(String[] argv) throws Exception {
int reps = 10;
if (argv.length > 0) {
//输入的数据代表输出文件的行数,缺省值为10
reps = Integer.parseInt(argv[0]);
}
FileOutputStream fos = new FileOutputStream(outputFile);
//获取GatheringByteChannel
GatheringByteChannel gatherChannel = fos.getChannel();
// utterBS方法的作用是返回将要写在文件中的内容的字节缓冲数组
ByteBuffer[] bs = utterBS(reps);
// 此循环的作用是将所有在通道里的内容输出
while (gatherChannel.write(bs) > 0) {
}
System.out.println("Mindshare paradigms synergized to " + outputFile);
fos.close();
}
private static String[] col1 = { "Aggregate", "Enable", "Leverage",
"Facilitate", "Synergize", "Repurpose", "Strategize", "Reinvent",
"Harness" };
private static String[] col2 = { "cross-platform", "best-of-breed",
"frictionless", "ubiquitous", "extensible", "compelling",
"mission-critical", "collaborative", "integrated" };
private static String[] col3 = { "methodologies", "infomediaries",
"platforms", "schemas", "mindshare", "paradigms",
"functionalities", "web services", "infrastructures" };
private static String newline = System.getProperty("line.separator");
private static ByteBuffer[] utterBS(int howMany) throws Exception {
List list = new LinkedList();
for (int i = 0; i < howMany; i++) {
//pickRandom为获取随机数的操作
list.add(pickRandom(col1, " "));
list.add(pickRandom(col2, " "));
list.add(pickRandom(col3, newline));
}
ByteBuffer[] bufs = new ByteBuffer[list.size()];
//将list转换成数组存入bufs
list.toArray (bufs);
return (bufs);
}
private static Random rand = new Random();
private static ByteBuffer pickRandom(String[] strings, String suffix)
throws Exception {
//获取随机字符串
String string = strings[rand.nextInt(strings.length)];
int total = string.length() + suffix.length();
ByteBuffer buf = ByteBuffer.allocate(total);
buf.put(string.getBytes("US-ASCII"));
buf.put(suffix.getBytes("US-ASCII"));
buf.flip();
return (buf);
}
}
这段程序可以大概描述出scatter/gather的大致思想,即同时读取多个缓冲区和同时写入多个缓冲区
以上是channel的基础部分,之后我们将进一步研究以后会广泛应用的file通道和socket通道