这次讲的是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:通过通道进行复制 * * : Cheney * 2016年5月22日 上午10:32:02 * 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: * * : Cheney * 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通道