代码改变世界

Java IO best practice

2011-02-23 21:24  RayLee  阅读(449)  评论(0编辑  收藏  举报

A StreamCopier class that copies data between two streams as quickly as possible. The method reads from the input stream and writes onto the output stream until the input stream is exhausted. A 1K buffer is used to try to make the reads more efficient.

class StreamCopier {
    public static void copy(InputStream in, OutputStream out) 
        throws IOException {
        byte[] buffer = new byte[1024];
        while (true) {
            int bytesRead = in.read(buffer);
            if (bytesRead == -1) break;
            out.write(buffer, 0, bytesRead);
        }
    }
}

Sun随后发布了NIO(Nonblocking I/O),它在处理大文件时(如大于1G的文件)性能有显著提高。下面使用NIO实现文件拷贝。

class FileCopier {
    public static void copy(String inFile, String outFile) {
        FileInputStream fis = new FileInputStream(inFile);
        FileOutputStream fos = new FileOutputStream(outFile);
        FileChannel inChannel = fis.getChannel();
        FileChannel outChannel = fos.getChannel();
        for (ByteBuffer buf = ByteBuffer.allocate(1024*1024);
             inChannel.read(buf) != -1;
             buf.clear()) {
            buf.flip();
            while (buf.hasRemaining())
                outChannel.write(buf);
        }

        inChannel.close();
        outChannel.close();
    }
}

前面两个拷贝的方法都存在一个bug,没有考虑到源文件和目标文件相同的情况。

下面的例子解决了该bug

class SafeFileCopier {
    public static void copy(File inFile, File outFile) throws IOException {
        if (inFile.getCanonicalPath().equals(outFile.getCanonicalPath( ))) {
            // inFile and outFile are the same, hence no copying is required
            return;
        }

        InputStream in = null;
        OutputStream out = null;
        try {
            in = new BufferedInputStream(new FileInputStream(inFile));
            out = new BufferedOutputStream(new FileOutputStream(outFile));
            for (int c = in.read(); c != -1; c = in.read( )) {
                out.write(c);
            }
        } finally {
            if (in != null) in.close();
            if (out != null) out.close();
        }
    }
}

getCanonicalPath()会将一些符号链接,快捷方式等解析为真正的目标文件,因此确定了文件的唯一性。

网络服务器对IO的处理
网络服务器会收到来自成千上百用户的请求,为了不阻塞用户,通常为每一个用户创建一个线程。如果创建非常多的线程,服务器性能会显著下降。NIO中一些新的API既解决了阻塞问题,又解决了性能问题。

public class Demo {
    private static byte[] data = new byte[256];
    
    public static void main(String[] args) throws IOException {
        for (int i = 0; i < data.length; i++)
            data[i] = (byte) i;
        
        ServerSocketChannel server = ServerSocketChannel.open();
        server.configureBlocking(false);
        server.socket().bind(new InetSocketAddress(9000));
        Selector selector = Selector.open();
        server.register(selector, SelectionKey.OP_ACCEPT);
        while (true) {
            selector.select();
            Set readyKeys = selector.selectedKeys();
            Iterator iterator = readyKeys.iterator();
            while (iterator.hasNext()) {
                SelectionKey key = (SelectionKey) iterator.next();
                iterator.remove();
                try {
                    if (key.isAcceptable()) {
                        SocketChannel client = server.accept();
                        System.out.println("Accepted connection from " + client);
                        client.configureBlocking(false);
                        ByteBuffer source = ByteBuffer.wrap(data);
                        SelectionKey key2 = client.register(selector,
                                SelectionKey.OP_WRITE);
                        key2.attach(source);
                    } else if (key.isWritable()) {
                        SocketChannel client = (SocketChannel) key.channel();
                        ByteBuffer output = (ByteBuffer) key.attachment();
                        if (!output.hasRemaining()) {
                            output.rewind();
                        }
                        client.write(output);
                    }
                } catch (IOException ex) {
                    key.cancel();
                    try {
                        key.channel().close();
                    } catch (IOException cex) {
                    }
                }
            }
        }
    }
}