当遇到并发服务场景时,咱们能够采取如下措施:


一、同步阻塞IO实现

public class DemoServer extends Thread {    private ServerSocket serverSocket;    public int getPort() {        return serverSocket.getLocalPort();    }    public void run() {        try {            serverSocket = new ServerSocket(0);            while (true) {                // 十分占用内存资源,每个客户端启用一个线程,非常不合理                Socket socket = serverSocket.accept();                RequesHandler requesHandler = new RequesHandler(socket);                requesHandler.start();            }        } catch (IOException e) {            e.printStackTrace();        } finally {            if (serverSocket != null) {                try {                    serverSocket.close();                } catch (IOException e) {                    e.printStackTrace();                }                ;            }        }    }    public static void main(String[] args) throws IOException {        DemoServer server = new DemoServer();        server.start();        try (Socket client = new Socket(InetAddress.getLocalHost(), server.getPort())) {            BufferedReader buferedReader = new BufferedReader(new InputStreamReader(client.getInputStream()));            buferedReader.lines().forEach(s -> System.out.println(s));        }    }}// 简化实现,不做读取,间接发送字符串class RequesHandler extends Thread {    private Socket socket;    RequesHandler(Socket socket) {        this.socket = socket;    }    @Override    public void run() {        try (PrintWriter out = new PrintWriter(socket.getOutputStream());) {            out.println("Hello world!");            out.flush();        } catch (Exception e) {            e.printStackTrace();        }    }}

每次 new 一个线程或者销毁一个线程是有显著的开销的,每个线程都有独自的线程构造,十分占用内存资源,每个客户端启用一个线程是非常不合理的, 因而能够采纳线程池的形式进行优化。

// 也是阻塞IO,采纳线程池的形式解决申请,当来一个新的客户端连贯时,// 将申请 Socket 封装成一个 task ,放到线程池中取执行。serverSocket = new ServerSocket(0);executor = Executors.newFixedThreadPool(8);while (true) {    Socket socket = serverSocket.accept();    RequesHandler requesHandler = new RequesHandler(socket);    executor.execute(requesHandler);}

二、NIO实现

NIO(非阻塞IO) 多路复用机制

public class NIOServer extends Thread {    public void run() {        try (Selector selector = Selector.open(); ServerSocketChannel serverSocket = ServerSocketChannel.open();) {// 创立Selector和Channel            serverSocket.bind(new InetSocketAddress(InetAddress.getLocalHost(), 8888));            serverSocket.configureBlocking(false);            // 注册到Selector,并阐明关注点            serverSocket.register(selector, SelectionKey.OP_ACCEPT);            while (true) {                selector.select();// 阻塞期待就绪的Channel,这是关键点之一                Set<SelectionKey> selectedKeys = selector.selectedKeys();                Iterator<SelectionKey> iter = selectedKeys.iterator();                while (iter.hasNext()) {                    SelectionKey key = iter.next();                    // 生产零碎中个别会额定进行就绪状态查看                    sayHelloWorld((ServerSocketChannel) key.channel());                    iter.remove();                }            }        } catch (IOException e) {            e.printStackTrace();        }    }    private void sayHelloWorld(ServerSocketChannel server) throws IOException {        try (SocketChannel client = server.accept();) {            ByteBuffer readBuffer = ByteBuffer.allocate(32);            client.read(readBuffer);            System.out.println("Server received : " + new String(readBuffer.array()));            ByteBuffer writeBuffer = ByteBuffer.allocate(128);            writeBuffer.put("hello xiaoming".getBytes());            writeBuffer.flip();            client.write(writeBuffer);            //client.write(Charset.defaultCharset().encode("Hello world!"));        }    }    public static void main(String[] args) throws IOException {        NIOServer server = new NIOServer();        server.start();                try {            SocketChannel socketChannel = SocketChannel.open();            socketChannel.connect(new InetSocketAddress(InetAddress.getLocalHost(), 8888));            ByteBuffer writeBuffer = ByteBuffer.allocate(32);            ByteBuffer readBuffer = ByteBuffer.allocate(32);            writeBuffer.put("hello".getBytes());            writeBuffer.flip();            while (true) {                writeBuffer.rewind();                socketChannel.write(writeBuffer);//                readBuffer.clear();                socketChannel.read(readBuffer);                System.out.println("Client received : " + new String(readBuffer.array()));            }        } catch (IOException e) {        }            }/** * @return */private int getPort() {    return 8888;}

三、总结

在后面两个例子中:阻塞IO和伪异步IO,一个是应用 new 线程的形式,另外一个是采纳线程池治理的形式, IO都是同步阻塞模式,所以须要多线程以实现多任务处理。

而 NIO 则是利用了单线程轮询事件的机制,通过高效地定位就绪的Channel,来决定做什么,仅仅select阶段是阻塞的,能够无效防止大量客户端连贯时频繁线程切换带来的问题,利用的扩大能力有了十分大的进步。