版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/lzx_longyou/article/details/51980285
文件锁定
要获取文件的一部分上的锁,需要调用FileChannel上的locak()方法。注意,如果要获取一个排它锁,您必须以写方式打开文件。
第二行中调用lock方法来获取锁,position参数表示锁的起始位置,size表示锁的范围,shared是一个boolean值,true表示是一个共享锁,false表示是一个排它锁。
FileChannel channel = out.getChannel();
FileLock lock = channel.lock(position, size, shared);
lock.release();
异步I/O
通常程序中调用read()方法时,程序会阻塞直至有可供读取的数据。同样,调用write()将会阻塞直至数据能够写入。而异步I/O是一种没有阻塞的读写数据的方法,我们将感兴趣的特性的I/O事件注册到选择器上面,当这些事件发生时,系统将会告诉我们。异步 I/O 的一个优势在于,它允许您同时根据大量的输入和输出执行 I/O。同步程序常常要求助于轮询,或者创建许许多多的线程以处理大量的连接。使用异步 I/O,您可以监听任何数量的通道上的事件,不用轮询,也不用额外的线程。
异步I/O中的核心对象是Selector,表示一个选择器,它就是我们注册感兴趣I/O事件的地方,当这些事件发生,这个选择器就会通知我们。每次返回主循环,我们都要调用 select 的 Selector()方法,并取得一组 SelectionKey。每个键代表一个 I/O 事件。我们处理事件,从选定的键集中删除 SelectionKey,然后返回主循环的顶部。
public class MultiPortEcho {
private int ports[]; //用于保存端口号
private ByteBuffer echoBuffer = ByteBuffer.allocate(1024);
public MultiPortEcho(int ports[]) throws IOException {
this.ports = ports;
go();
}
private void go() throws IOException {
//创建一个选择器对象
Selector selector = Selector.open();
//在每个端口上开启监听,绑定端口号,并注册到选择器上
for (int i = 0; i < ports.length; ++i) {
ServerSocketChannel ssc = ServerSocketChannel.open();
//设置为false表示非阻塞
ssc.configureBlocking(false);
ServerSocket ss = ssc.socket();
InetSocketAddress address = new InetSocketAddress(ports[i]);
ss.bind(address);
//将新打开的 ServerSocketChannels 注册到 Selector上
//SelectionKey 代表这个通道在此 Selector 上的这个注册。
//当某个 Selector 通知您某个传入事件时,它是通过提供对应于该事件的 SelectionKey 来进行的。
//SelectionKey 还可以用于取消通道的注册。
SelectionKey key = ssc.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("Going to listen on " + ports[i]);
}
while (true) {
//这个方法会阻塞,直到至少有一个已注册的事件发生。
//当一个或者更多的事件发生时, select() 方法将返回所发生的事件的数量。
int num = selector.select();
Set selectedKeys = selector.selectedKeys();
Iterator it = selectedKeys.iterator();
while (it.hasNext()) {
SelectionKey key = (SelectionKey) it.next();
if ((key.readyOps() & SelectionKey.OP_ACCEPT) == SelectionKey.OP_ACCEPT) {
//接受新连接
ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
SocketChannel sc = ssc.accept();
//将新连接设置为非阻塞
sc.configureBlocking(false);
//将连接注册到选择器上, OP_READ 参数表示将 SocketChannel注册用于读取而不是接受新连接。
SelectionKey newKey = sc.register(selector, SelectionKey.OP_READ);
//将处理过的 SelectionKey 从选定的键集合中删除
//如果我们没有删除处理过的键,那么它仍然会在主集合中以一个激活的键出现,这会导致我们尝试再次处理它.
it.remove();
System.out.println("Got connection from " + sc);
} else if ((key.readyOps() & SelectionKey.OP_READ) == SelectionKey.OP_READ) {
//接收数据
SocketChannel sc = (SocketChannel) key.channel();
int bytesEchoed = 0;
while (true) {
echoBuffer.clear();
int r = sc.read(echoBuffer);
if (r <= 0) {
break;
}
echoBuffer.flip();
sc.write(echoBuffer);
bytesEchoed += r;
}
System.out.println("Echoed " + bytesEchoed + " from " + sc);
it.remove();
}
}
}
}
public static void main(String args[]) throws Exception {
if (args.length <= 0) {
System.err.println("Usage: java MultiPortEcho port [port port ...]");
System.exit(1);
}
int ports[] = new int[args.length];
for (int i = 0; i < args.length; ++i) {
ports[i] = Integer.parseInt(args[i]);
}
new MultiPortEcho(ports);
}
}