NIO--Channel

类结构图

接口

Channel

public interface Channel extends Closeable {
    public boolean isOpen();
    public void close() throws IOException;

}

InterruptibleChannel

标记接口,表示支持中断

AsynchronousChannel

标记接口,表示支持异步。

public interface AsynchronousChannel     extends Channel
{
    @Override
    void close() throws IOException;
}

NetworkChannel

具有网络功能。

public interface NetworkChannel     extends Channel
{
    NetworkChannel bind(SocketAddress local) throws IOException;
    SocketAddress getLocalAddress() throws IOException;

    <T> NetworkChannel setOption(SocketOption<T> name, T value) throws IOException;
    <T> T getOption(SocketOption<T> name) throws IOException;
    Set<SocketOption<?>> supportedOptions();
}

MulticastChannel

具有多播功能

public interface MulticastChannel     extends NetworkChannel
{
    @Override 
    void close() throws IOException;

    MembershipKey join(InetAddress group, NetworkInterface interf)         throws IOException;
    MembershipKey join(InetAddress group, NetworkInterface interf, InetAddress source)         throws IOException;
}

ReadableByteChannel

增加了read(...)方法。

public interface ReadableByteChannel extends Channel {
    public int read(ByteBuffer dst) throws IOException;

}

ScatteringByteChannel

支持从多个ByteBuffer中read数据。

public interface ScatteringByteChannel     extends ReadableByteChannel
{
    public long read(ByteBuffer[] dsts, int offset, int length)        throws IOException;
    public long read(ByteBuffer[] dsts) throws IOException;
}

WritableByteChannel

增加了write(...)方法。

public interface WritableByteChannel     extends Channel
{
    public int write(ByteBuffer src) throws IOException;
}

GatheringByteChannel

支持把数据write到多个ByteBuffer中。

public interface GatheringByteChannel
    extends WritableByteChannel
{
    public long write(ByteBuffer[] srcs, int offset, int length)         throws IOException;
    public long write(ByteBuffer[] srcs) throws IOException;

}

ByteChannel

标记接口,表示具有read,write功能。

public interface ByteChannel     extends ReadableByteChannel, WritableByteChannel
{

}

SeekableByteChannel

具有定位方法。


public interface SeekableByteChannel     extends ByteChannel
{
    @Override
    int read(ByteBuffer dst) throws IOException;

    @Override
    int write(ByteBuffer src) throws IOException;

    //**********定位方法**********
    long position() throws IOException;
    SeekableByteChannel position(long newPosition) throws IOException;
    long size() throws IOException;
    SeekableByteChannel truncate(long size) throws IOException;
}

实现类

I/O可以分为广义的两大类别:File I/O和StreamI/O。那么相应地通道也有两种类型,它们是文件(file)通道套接字(socket)通道。套接字(socket)通道又分为:SocketChannel、ServerSocketChannel和DatagramChannel。

FileChannel

文件通道总是阻塞式的,因此不能被置于非阻塞模式。

FileChannel对象不能直接创建。一个FileChannel实例只能通过在一个打开的file对象(RandomAccessFileFileInputStreamFileOutputStream)上调用getChannel()方法获取。调用getChannel( )方法会返回一个连接到相同文件的FileChannel对象且该FileChannel对象具有与file对象相同的访问权

FileChannel对象是线程安全(thread-safe)的。多个进程可以在同一个实例上并发调用方法而不会引起任何问题,不过并非所有的操作都是多线程的(multithreaded)。影响通道位置或者影响文件大小的操作都是单线程的(single-threaded)。如果有一个线程已经在执行会影响通道位置或文件大小的操作,那么其他尝试进行此类操作之一的线程必须等待。并发行为也会受到底层的操作系统或文件系统影响。

文件访问

position值决定文件中哪一处的数据接下来将被读或者写。

将通道position设置为一个负值会导致java.lang.IllegalArgumentException异常

把position设置到超出文件尾,这样做会把position设置为指定值而不改变文件大小。假如在将position设置为超出当前文件大小时实现了一个read()方法,那么会返回一个文件尾(end-of-file)条件;倘若此时实现的是一个write( )方法则会引起文件增长以容纳写入的字节,具体行为类似于实现一个绝对write( )并可能导致出现一个文件空洞(filehole)。

FileChannel位置(position)是从底层的文件描述符获得的,该position同时被作为通道引用获取来源的文件对象共享。这也就意味着一个对象对该position的更新可以被另一个对象看到

带position参数的绝对形式的read( )和write( )方法在返回值时不会改变当前的文件position。

truncate( )方法会砍掉您所指定的新size值之外的所有数据。文件的position会被设置为所提供的新size值。

force( )方法要求文件的所有待定修改立即同步到磁盘

文件锁定

锁的对象是文件而不是通道或线程,这意味着文件锁不适用于判优同一台Java虚拟机上的多个线程发起的访问。

如果一个线程在某个文件上获得了一个独占锁,然后第二个线程利用一个单独打开的通道来请求该文件的独占锁,那么第二个线程的请求会被批准。但如果这两个线程运行在不同的Java虚拟机上,那么第二个线程会阻塞,因为锁最终是由操作系统或文件系统来判优的并且几乎总是在进程级而非线程级上判优锁都是与一个文件关联的,而不是与单个的文件句柄或通道关联。

public abstract FileLock lock(long position, long size, boolean shared)         throws IOException;
public final FileLock lock() throws IOException {
    return lock(0L, Long.MAX_VALUE, false);
}
public abstract FileLock tryLock(long position, long size, boolean shared)     throws IOException;
    
public final FileLock tryLock() throws IOException {
    return tryLock(0L, Long.MAX_VALUE, false);
}

锁是在文件内部区域上获得的。

要获得一个共享锁,必须先以只读权限打开文件,而请求独占锁时则需要写权限。

锁定区域的范围不一定要限制在文件的size值以内,锁可以扩展从而超出文件尾。因此,可以提前把待写入数据的区域锁定,也可以锁定一个不包含任何文件内容的区域,比如文件最后一个字节以外的区域。如果之后文件增长到达那块区域,那么文件锁就可以保护该区域的文件内容了。相反地,如果锁定了文件的某一块区域,然后文件增长超出了那块区域,那么新增加的文件内容将不会受到文件锁的保护。

tryLock( )的方法是lock( )方法的非阻塞变体。和lock( )方法起相同的作用,不过如果请求的锁不能立即获取到则会返回一个null

FileLock

FileLock类封装一个锁定的文件区域。FileLock对象由FileChannel创建并且总是关联到那个特定的通道实例。

public abstract class FileLock
{
    public final FileChannel channel( ); 
    public final long position( ); 
    public final long size( ) ;
    public final boolean isShared( ) ;
    public final boolean overlaps (long position, long size) ;
    public abstract boolean isValid( ); 
    public abstract void release( ) throws IOException; 
}

channel( )方法来查询一个lock对象以判断它是由哪个通道创建的

一个FileLock对象创建之后即有效,直到它的release()方法被调用或它所关联的通道被关闭或Java虚拟机关闭时才会失效。我们可以通过调用isValid()布尔方法来测试一个锁的有效性。一个锁的有效性可能会随着时间而改变,不过它的其他属性——位置(position)、范围大小(size)和独占性(exclusivity)——在创建时即被确定,不会随着时间而改变。

isShared()方法来测试一个锁以判断它是共享的还是独占的。如果底层的操作系统或文件系统不支持共享锁,那么该方法将总是返回false值。

overlaps( )方法来查询一个FileLock对象是否与一个指定的文件区域重叠。这将使您可以迅速判断您拥有的锁是否与一个感兴趣的区域(region of interest)有交叉

使用完一个锁后而不释放它的话,可能会导致冲突或者死锁

内存映射文件

    public abstract MappedByteBuffer map(MapMode mode,long position, long size)         throws IOException;

map()的方法,该方法可以在一个打开的文件和一个特殊类型的ByteBuffer之间建立一个虚拟内存映射。它会创建一个由磁盘文件支持的虚拟内存映射(virtual memory mapping)并在那块虚拟内存空间外部封装一个MappedByteBuffer对象

MappedByteBuffer对象的行为在多数方面类似一个基于内存的缓冲区,只不过该对象的数据元素存储在磁盘上的一个文件中。调用get()方法会从磁盘文件中获取数据,此数据反映该文件的当前内容。相似地,对映射的缓冲区实现一个put()会更新磁盘上的那个文件,并且您做的修改对于该文件的其他阅读者也是可见的。

通过内存映射机制来访问一个文件会比使用常规方法读写高效得多,甚至比使用通道的效率都高。因为不需要做明确的系统调用,那会很消耗时间。更重要的是,操作系统的虚拟内存可以自动缓存内存页(memory page)。这些页是用系统内存来缓存的,所以不会消耗Java虚拟机内存堆(memory heap)。

与文件锁的范围机制不一样,映射文件的范围不应超过文件的实际大小。如果请求一个超出文件大小的映射,文件会被增大以匹配映射的大小。即使您请求的是一个只读映射,map( )方法也会尝试这样做。

映射模式:

  • READ_ONLY
  • READ_WRITE
  • PRIVATE:写时拷贝(copy-on-write)的映射

写时拷贝(copy-on-write)的映射。这意味着您通过put()方法所做的任何修改都会导致产生一个私有的数据拷贝并且该拷贝中的数据只有MappedByteBuffer实例可以看到。该过程不会对底层文件做任何修改,而且一旦缓冲区被施以垃圾收集动作(garbagecollected),那些修改都会丢失。尽管写时拷贝的映射可以防止底层文件被修改,您也必须以read/write权限来打开文件以建立MapMode.PRIVATE映射。只有这样,返回的MappedByteBuffer对象才能允许使用put( )方法。

映射缓冲区没有绑定到创建它们的通道上。关闭相关联的FileChannel不会破坏映射,只有丢弃缓冲区对象本身才会破坏该映射。

Channel-to-Channel传输

由于经常需要从一个位置将文件数据批量传输到另一个位置,FileChannel类添加了一些优化方法来提高该传输过程的效率:

public abstract long transferFrom(ReadableByteChannel src, long position, long count)throws IOException;
public abstract long transferTo(long position, long count, WritableByteChannel target)throws IOException;

transferTo( )transferFrom( )方法允许将一个通道交叉连接到另一个通道,而不需要通过一个中间缓冲区来传递数据。只有FileChannel类有这两个方法,因此channel-to-channel传输中通道之一必须是FileChannel

直接的通道传输不会更新与某个FileChannel关联的position值。请求的数据传输将从position参数指定的位置开始,传输的字节数不超过count参数的值。实际传输的字节数会由方法返回,可能少于您请求的字节数。

示例:

private static void catFiles (WritableByteChannel target, String [] files) throws Exception

{

for (int i = 0; i < files.length; i++)
{
     FileInputStream fis = new FileInputStream (files [i]);
     FileChannel channel = fis.getChannel( );
     channel.transferTo (0, channel.size( ), target);
     channel.close( );
     fis.close( );
}

}

Socket通道

socket通道类可以运行非阻塞模式并且是可选择的.

DatagramChannel和SocketChannel实现定义读和写功能的接口而ServerSocketChannel不实现。ServerSocketChannel负责监听传入的连接和创建新的SocketChannel对象,它本身从不传输数据。

全部socket通道类(DatagramChannel、SocketChannel和ServerSocketChannel)在被实例化时都会创建一个对等socket对象

非阻塞模式

Socket通道可以在非阻塞模式下运行。传统Java socket是阻塞的。要把一个socket通道置于非阻塞模式,我们要依靠所有socket通道类的公有超级类:SelectableChannel。


public abstract class SelectableChannel extends AbstractInterruptibleChannel implements Channel
{
    ...
    ...

    public abstract SelectableChannel configureBlocking(boolean block) throws IOException;

    public abstract boolean isBlocking();

    public abstract Object blockingLock();

}

configureBlocking()方法设置或重新设置一个通道的阻塞模式,参数值为true则设为阻塞模式,参数值为false值设为非阻塞模式。
isBlocking( )方法来判断某个socket通道当前处于哪种模式

SocketChannel sc = SocketChannel.open( );

sc.configureBlocking (false);

// nonblocking ...

if ( ! sc.isBlocking( ))

{ doSomething (cs); }

blockingLock()方法,防止socket通道的阻塞模式被更改。该方法会返回一个非透明的对象引用。返回的对象是通道实现修改阻塞模式时内部使用的。只有拥有此对象的锁的线程才能更改通道的阻塞模式(对象的锁是用同步的Java密码获取的,它不同于lock( )方法)

Socket socket = null;
Object lockObj = serverChannel.blockingLock( );
// have a handle to the lock object, but haven't locked it yet
// may block here until lock is acquired
synchronize (lockObj) {
         // This thread now owns the lock; mode can't be changed
         boolean prevState = serverChannel.isBlocking( );
         serverChannel.configureBlocking (false);
        socket = serverChannel.accept( );
        serverChannel.configureBlocking (prevState);
 }
 // lock is now released, mode is allowed to change
 if (socket != null)
 {
 doSomethingWithTheSocket (socket);
 }

ServerSocketChannel

ServerSocketChannel是一个基于通道的socket监听器。同我java.net.ServerSocket执行相同的基本任务,不过它增加了通道语义,因此能够在非阻塞模式下运行。

用静态的open()工厂方法创建一个新的ServerSocketChannel对象,将会返回同一个未绑定的java.net.ServerSocket关联的通道。该对等ServerSocket可以通过在返回的ServerSocketChannel上调用socket( )方法来获取。

 ServerSocketChannel ssc = ServerSocketChannel.open( );
 ServerSocket serverSocket = ssc.socket( );
 serverSocket.bind (new InetSocketAddress (1234));

accept( )方法在阻塞模式下总是阻塞并返回一个java.net.Socket对象,返回的对象能够在非阻塞模式下运行

如果以非阻塞模式被调用,当没有传入连接在等待时,ServerSocketChannel.accept( )会立即返回null

 ServerSocketChannel ssc = ServerSocketChannel.open( );
 ServerSocket serverSocket = ssc.socket( );
 serverSocket.bind (new InetSocketAddress (1234));

 
 ByteBuffer buffer = ByteBuffer.wrap (GREETING.getBytes( ));
 ServerSocketChannel ssc = ServerSocketChannel.open( );
 
 ssc.socket( ).bind (new InetSocketAddress (port));

//非阻塞模式
 ssc.configureBlocking (false);
 while (true)
 {
     ...
     SocketChannel sc = ssc.accept( );
     if (sc == null) {
     ...
     } else {
     ...
     }
 }

SocketChannel

open( )方法可以创建一个新的SocketChannel对象,而在新创建的SocketChannel上调用socket( )方法能返回它对等的Socket对象;在该Socket上调用getChannel( )方法则能返回最初的那个SocketChannel。

新创建的SocketChannel虽已打开却是未连接的。可以通过在通道上直接调用connect( )方法或在通道关联的Socket对象上调用connect( )来将该socket通道连接。isConnected( )方法来测试某个SocketChannel当前是否已连接

调用finishConnect( )方法来完成连接过程,该方法任何时候都可以安全地进行调用。假如在一个非阻塞模式的SocketChannel对象上调用finishConnect( )方法,将可能出现下列情形之一:

  •  connect( )方法尚未被调用。那么将产生NoConnectionPendingException异常。
  • 连接建立过程正在进行,尚未完成。那么什么都不会发生,finishConnect( )方法会立即返回false值。
  • 在非阻塞模式下调用connect( )方法之后,SocketChannel又被切换回了阻塞模式。那么如果有必要的话,调用线程会阻塞直到连接建立完成,finishConnect( )方法接着就会返回true值。
  • 在初次调用connect( )或最后一次调用finishConnect( )之后,连接建立过程已经完成。那么SocketChannel对象的内部状态将被更新到已连接状态,finishConnect( )方法会返回true值,然后SocketChannel对象就可以被用来传输数据了。
  • 连接已经建立。那么什么都不会发生,finishConnect( )方法会返回true值。

Socket通道是线程安全的。并发访问时无需特别措施来保护发起访问的多个线程,不过任何时候都只有一个读操作和一个写操作在进行中。

connect( )和finishConnect( )方法是互相同步,并且只要其中一个操作正在进行,任何读或写的方法调用都会阻塞,即使是在非阻塞模式下

DatagramChannel

SocketChannel模拟连接导向的流协议(如TCP/IP),DatagramChannel则模拟包导向的无连接协议(如UDP/IP)

DatagramChannel对象既可以充当服务器(监听者)也可以充当客户端(发送者)

DatagramChannel是无连接的

管道

管道就是一个用来在两个实体之间单向传输数据的导管

public abstract class Pipe {
    protected Pipe() { }
    public abstract SourceChannel source();
    public abstract SinkChannel sink();
    public static Pipe open() throws IOException {
        return SelectorProvider.provider().openPipe();
    }
}

Pipe类创建一对提供环回机制的Channel对象。这两个通道的远端是连接起来的,以便任何写在SinkChannel对象上的数据都能出现在SourceChannel对象。唯一可保证的是写到SinkChannel中的字节都能按照同样的顺序在SourceChannel上重现。


Pipe pipe = Pipe.open( );
WritableByteChannel w = pipe.sink( );
ReadableByteChannel r = pipe.source( );

w.write (buffer);

.......
r.read (buffer);

通道工具类

java.nio.channels.Channels

方法返回描述
newChannel (InputStream in) ReadableByteChannel返回一个将从给定的输入流读取数据的通道。
newChannel (OutputStream out) WritableByteChannel返回一个将向给定的输出流写入数据的通道。
newInputStream (ReadableByteChannel ch) InputStream 返回一个将从给定的通道读取字节的流。
newOutputStream (WritableByteChannel ch) OutputStream返回一个将向给定的通道写入字节的流。
newReader (ReadableByteChannel ch, CharsetDecoder dec, int minBufferCap) Reader 返回一个reader,它将从给定的通道读取字节并依据提供的CharsetDecoder对读取到的字节进行解码
newReader(ReadableByteChannel ch, String csName) Reader 返回一个reader,它将从给定的通道读取字节并依据提供的字符集名称将读取到的字节解码成字符。
newWriter (WritableByteChannel ch, CharsetEncoder dec, int minBufferCap) Writer返回一个writer,它将使用提供的CharsetEncoder对象对字符编码并写到给定的通道中。
newWriter (WritableByteChannel ch, String csName) Writer 返回一个writer,它将依据提供的字符集名称对字符编码并写到给定的通道中。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值