http://tutorials.jenkov.com/java-nio/buffers.html
IO
一个例子
public class DemoServer extends Thread { private ServerSocket serverSocket; public int getPort() { return serverSocket.getLocalPort(); } @Override public void run() { try { serverSocket = new ServerSocket(0); // 绑定0端口 while (true) { Socket socket = serverSocket.accept(); // 调用accept方法,阻塞等待客户端连接 RequestHandler requestHandler = new RequestHandler(socket); requestHandler.start(); } } catch (IOException e) { e.printStackTrace(); } finally { if (serverSocket != null) { try { serverSocket.close(); } catch (IOException e) { e.printStackTrace(); } } } } public static void main(String[] args) { DemoServer server = new DemoServer(); server.start(); try (Socket client = new Socket(InetAddress.getLocalHost(), server.getPort())) { // 模拟socket客户端 BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(client.getInputStream())); bufferedReader.lines().forEach(s -> System.out.println(s)); } catch (Exception e) { e.printStackTrace(); } } } class RequestHandler extends Thread { private Socket socket; RequestHandler(Socket socket) { this.socket = socket; } @Override public void run() { try (PrintWriter out = new PrintWriter(socket.getOutputStream());) { out.println("hello"); out.flush(); } catch (Exception e) { e.printStackTrace(); } } }
线程启动
每个都开一个线程过于浪费,线程池改进
serverSocket = new ServerSocket(0); executorService = Executors.newFixedThreadPool(8); while (true) { Socket socket = serverSocket.accept(); RequestHandler requestHandler = new RequestHandler(socket); // requestHandler.start(); executorService.execute(requestHandler); }
并发量不大的情况这种同步阻塞方式是可以的,但是由于线程的上下文切换是废性能的,在高并发情况下显然不合适。
NIO例子
public class NIOServer extends Thread { @Override public void run() { try (Selector selector = Selector.open(); ServerSocketChannel serverSocket = ServerSocketChannel.open();) { serverSocket.bind(new InetSocketAddress(InetAddress.getLocalHost(), 8888)); serverSocket.configureBlocking(false); serverSocket.register(selector, SelectionKey.OP_ACCEPT); while (true) { selector.select(); Set<SelectionKey> selectionKeys = selector.selectedKeys(); Iterator<SelectionKey> iter = selectionKeys.iterator(); while (iter.hasNext()) { SelectionKey key = iter.next(); sayHello((ServerSocketChannel) key.channel()); iter.remove(); } } } catch (Exception e) { e.printStackTrace(); } } private void sayHello(ServerSocketChannel server) { try (SocketChannel client = server.accept()) { client.write(Charset.defaultCharset().encode("hello")); } catch (Exception e) { e.printStackTrace(); } } }
NIO利用了单线程轮询事件的机制,通过高效地定位就绪的Channel,来决定做什 么,仅仅select阶段是阻塞的,可以有效避免大量客户端连接时,频繁线程切换带来的问题,应用的扩展能力有了非常大的提高。下面这张图对这种实现思路进行了形象地说明。
NIO2,异步IO
通过指定CompletionHandler回调接口,在accept/read/write等关键节点,通过事件机制调用。
文件拷贝
try (InputStream inputStream = new FileInputStream(source); OutputStream outputStream = new FileOutputStream(dest)) { byte[] buffer = new byte[1024]; int length; while ((length = inputStream.read(buffer)) > 0) { outputStream.write(buffer, 0, length); } } catch (IOException e) { e.printStackTrace(); }
try (FileChannel sourceChanel = new FileInputStream(source).getChannel(); FileChannel targetChanel = new FileOutputStream(dest).getChannel()) { for (long count = sourceChanel.size(); count > 0; ) { long transferred = sourceChanel.transferTo(sourceChanel.position(), count, targetChanel); sourceChanel.position(sourceChanel.position() + transferred); count -= transferred; } } catch (IOException e) { e.printStackTrace(); }
end
参照第一个链接可练习。