通道是NIO的一个主要创新,用于在Buffer与通道另外一端之间进行有效的数据传输,这点在NIO技术-1-缓冲区有讲过,这里不在赘述。java
I/O能够分为文件IO和流IO,那么通道对应的就能够分为文件通道(FileChannel)和流通道(流通道就是套接字通道,SocketChannel),因此NIO中有四种通道实现类:数组
打开一个通道的方法以下:安全
//打开一个文件通道,指定为可读写 RandomAccessFile raf = new RandomAccessFile("d:/test.txt", "rw"); FileChannel fc = raf.getChannel(); // 打开一个服务器套接字通道 ServerSocketChannel ssc = ServerSocketChannel.open(); // 打开一个套接字通道, SocketChannel sc = SocketChannel.open(); // 打开一个数据报通道 DatagramChannel dc = DatagramChannel.open();
文件通道还能够经过底层文件句柄的的方式得到,可是这样有可能致使不能读写文件服务器
//不要使用这种方式获取通道实例 FileInputStream fis = new FileInputStream("d:/test.txt"); FileChannel fileChannel = fis.getChannel(); ByteBuffer buff = ByteBuffer.allocate(8192); fileChannel.write(buff);
还能够经过java.nio.channels.Channels这个工具类获取通道实例,下面是一个例子:网络
// 建立一个可读通道 ReadableByteChannel rbc = Channels.newChannel(System.in); // 建立一个可写通道 WritableByteChannel wbc = Channels.newChannel(System.out); // 建立一个大小为8192字节的字节缓冲区 ByteBuffer buff = ByteBuffer.allocate(8192); // 轮询将可读通道的数据读到缓冲区 while (rbc.read(buff) != -1) { // 翻转缓冲区 buff.flip(); String str = new String(buff.array()).trim(); // 若输入"bye"则关闭通道 if (str.equals("bye")) { rbc.close(); wbc.close(); break; } // 将缓冲区的数据写入到可写通道 wbc.write(buff); // 轮询缓冲区是否还有剩余数据 while (buff.hasRemaining()) { wbc.write(buff); } // 清空缓冲区 buff.clear(); }
通道能够以阻塞(blocking)或非阻塞(non-blocking)模式运行,阻塞模式会一直等待某个操做直到返回结果;非阻塞不会一直等待,要么返回null,要么返回执行完的结果。只有流通道才能已non-blocking模式运行,如Socket和Pipe。并发
Socket通道类继承SelectableChannel,只有SelectableChannel类才能与选择器(Selector)一块儿使用。dom
关闭通道使用close()方法,调用close()方法根据操做系统的网络实现不一样可能会出现阻塞,能够在任什么时候候屡次调用close();若出现阻塞,第一次调用close()后会一直等待;若第一次调用close()成功关闭后,以后再调用close()会当即返回,不会执行任何操做。工具
在一个已关闭的通道上进行I/O操做会抛出ClosedChannelException,能够通道isOpen()方法来检查通道时候为打开状态。操作系统
NIO中的通道都实现了InterruptibleChannel,若某个线程上有一个处于阻塞状态的通道,线程被中断会抛出ClosedByInterruptException,并会关闭通道。能够调用isInterrupted()方法检查某个线程的interrupt状态。线程
1、分散读到多个缓冲区 & 从多个缓冲区汇集写入通道
//test.txt中只有一行数据:whoareu? //建立2个缓冲区,组成一个缓冲区数组 //打开一个文件通道,将test.txt中的数据读到缓冲区数组中 //缓冲区数组会自动填充buffA和buffB这两个缓冲区 //buffA填满后,再继续填充buffB //达到分散读取数据到多个缓冲区中 ByteBuffer buffA = ByteBuffer.allocate(6); ByteBuffer buffB = ByteBuffer.allocate(5); ByteBuffer[] buffArr = { buffA, buffB }; RandomAccessFile raf = new RandomAccessFile("test.txt", "r"); FileChannel fc = raf.getChannel(); fc.read(buffArr); System.out.println(new String(buffA.array()));//输出who System.out.println(new String(buffB.array()));//输出areu? fc.close(); raf.close();
//建立两个Buffer,组成一个Buffer Array //将Buffer Array的数据写入到文件通道到 //达到汇集缓冲区其中写入通道目的 byte[] byteA = "hello ".getBytes("UTF-8"); ByteBuffer buffC = ByteBuffer.wrap(byteA); byte[] byteB = "world!".getBytes("UTF-8"); ByteBuffer buffD = ByteBuffer.wrap(byteB); ByteBuffer[] allBuff = {buffC,buffD}; RandomAccessFile raf = new RandomAccessFile("test.txt", "rw"); FileChannel fc = raf.getChannel(); fc.write(allBuff); fc.close(); raf.close();
2、文件通道(FileChannel)
FileChannel不能直接建立,只能经过建立一个文件对象(RandAccessFile、FileInputStream、FileOutputStream)后调用其getChannel()方法得到。FileChannel是线程安全,多个进程并发操做同一文件不会引发任何问题;兵法行为受低层操做系统或文件系统影响。
FileChannel类保证同一个JVM上的全部FileChannel实例看到的文件内容是一致的,但不能保证外部的非Java进程看到的该文件视图一致,也可能一致,这取决于低层操做系统的实现。
打开一个文件通道可使用下面方式:
//RandomAccessFile有2中构造方法,下面的构造方法等同于: //RandomAccessFile raf = new RandomAccessFile(new File("test.txt"), "rw"); RandomAccessFile raf = new RandomAccessFile("test.txt", "rw"); FileChannel fc = raf.getChannel();
RandomAccessFile构造方法的第二个参数含义以下:
文件锁在Jdk1.4时才被提供,当多个程序并发操做同一个文件时,可使用文件锁来锁定文件同一时刻只能接受一个程序的IO操做。文件锁分为独占锁和共享锁,FileChannel提供了文件锁的API,在FileChannel的文件锁方式中,锁的对象是文件而不是通道或线程,也就是说文件锁不适用于同一个JVM上多个线程并发访问文件的状况。同一个JVM中,一个线程得到了某个文件的独占锁,第二个线程也能够得到这个文件的独占锁;可是,在不一样的JVM中,第一个JVM的线程得到的某个文件的独占所,第二个JVM的线程会被阻塞。致使这样的状况的缘由是文件锁是由操做系统在进程级上来判优的,而不是在线程级上。
文件锁能够经过FileChannel的lock()或tryLock()方法获取,二者的区别以下:
RandomAccessFile raf = new RandomAccessFile("test.txt", "rw"); FileChannel fc = raf.getChannel(); //-------------------------------------------- //阻塞得到文件独占锁,并锁定文件全部数据 FileLock lock0 = fc.lock(); //阻塞得到文件独占锁,并锁定文件指定数据 FileLock lock1 = fc.lock(0, 8192, false); //阻塞得到文件共享锁,并锁定文件0 ~ 8192字节的数据 FileLock lock2 = fc.lock(0, 8192, true); //-------------------------------------------- //非阻塞获取文件独占所,等同于lock() FileLock lock3 = fc.tryLock(); //非阻塞获取文件独占所,等同于fc.lock(0, 8192, false); FileLock lock4 = fc.tryLock(0, 8192, false); //非阻塞获取文件共享锁,等同于fc.lock(0, 8192, true); FileLock lock5 = fc.tryLock(0, 8192, true);
FileLock对象关联FileChannel,FileLock API以下:
实际应用中,通常使用共享锁读文件,使用独占锁写文件