传统的Socket是线程阻塞的,导致阻塞的原因有,sleep睡眠,wait等待,IO延迟等待、代码被同步等
1.非阻塞
      非阻塞指的是执行某些操作时,如果还没就绪,那么不会等待,立即返回,而等待事件的发生仍然是阻塞的
       JDK中java.nio包提供了对非阻塞通信的支持,常见的替代类如下:
              ServerSocketChannel 采用通道连接,替代ServerSocket
              SocketChannel  替代Socket
              Selector   选择器,用于监听以上产生的事件
              SelectorKey  通道向选择器注册事件的句柄,以此判断发生了什么事件
2.注册事件
       SelectorKey key = serverSocketChannel.register(selector,SelectionKey.OP_ACCEPT);
       表示事件的静态常量:
              SelectionKey.OP_ACCEPT:接收连接就绪事件
              SelectionKey.OP_CONNECT:连接就绪事件
              SelectionKey.OP_READ:读就绪事件
              SelectionKey.OP_WRITE:写就绪事件
3.缓冲区
       缓冲区的好处,减少物理读写次数,缓冲区内存一直被重用,减少了内存的分配和回收
       java.nio包提供了新的IO类
              BufferedInputStream,BufferedOutputStream,
              BufferedReader,BufferedWriter
       缓冲区的属性
              容量capacity:可以保存大小
              极限limit:缓冲区当前终点,不能对超过极限的区域进行读写
              位置position:用于读取数据
        缓冲区提供的方法
               clear:极限设为容量大小,位置设为0
               flip:极限设为位置大小,位置设为0
               rewind:不改变极限,位置设为0
               remaining:返回缓冲区剩余容量,即位置—极限
               compact:删除缓冲区0—位置的内容,位置—极限的内容复制到0—位置,极限变为容量大小
       Buffer类是一个抽象类
       最基本的缓冲区是ByteBuffer,获取示例的静态方法
               allocate(int Byte);分配空间,返回ByteBuffer对象
               directAllocate(int Byte);返回直接缓冲区,可以提高IO速度,但开销很大,缓冲区较大而且会长期存在
4.字符编码
       java.nio.Charset提供的编码和解码方法
              ByteBuffer encode(String str);字符串转字节序列存放在ByteBuffer对象中
              ByteBuffer encode(CharBuffer cb);字符缓冲区中的字符进行编码,然后将字节序列存放到ByteBuffer对象中
       CharBuffer decode(ByteBuffer bb);ByteBuffer字节序列解码,存放到CharBuffer对象中
       设置编码:Charset.forName("UTF_8");
       返回默认编码:defaultCharset();
5.通道
       通道是用来连接数据源和数据汇的,中间通过缓冲区
       Channel接口的方法
              close()关闭通道
              isOpen()判断通道是否打开
       通道创建时被打开,一旦关闭通道,就不能重新打开了
       Channel两个子接口
              ReadableByteChannel
              WritableByteChannel
6.SelectableChannel
       支持阻塞IO和非阻塞IO的通道
       主要方法
              configureBlocking(boolean);true表示设为阻塞模式
       两个继承类
              ServerSocketChannel
              SocketChannel

 7.摘取的源码

public class EchoServer {
	
	ServerSocketChannel serverSocketChannel;
	ExecutorService executorService;
	
	public EchoServer() throws Exception{
		// 根据CPU数创建线程池
		executorService = Executors.newFixedThreadPool(
				Runtime.getRuntime().availableProcessors()*2);
		// 创建ServerSocketChannel对象
		// 每个ServerSocketChannel对象都与一个ServerSocket对象关联
		// 此时没有与任何端口绑定并处于阻塞模式
		serverSocketChannel= ServerSocketChannel.open();
		// socket方法返回与之关联的ServerSocket对象,此处设置socket可以复用
		serverSocketChannel.socket().setReuseAddress(true);
		// 绑定端口
		serverSocketChannel.socket().bind(new InetSocketAddress(8080));
		System.out.println("服务器已启动");
	}
	
	
	public void service(){
		while(true){
			SocketChannel socketChannel = null;
			try {
				// 阻塞等待客户端
				socketChannel = serverSocketChannel.accept();
				// 提交给线程池执行,runnable一个任务
				executorService.execute(new Handler(socketChannel));
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
	
	public static void main(String[] args) throws Exception {
		new EchoServer().service();
	}
}

// 执行的业务逻辑,传入每个socket客户端
class Handler implements Runnable{

	private SocketChannel socketChannel;
	
	public Handler(SocketChannel socketChannel) {
		this.socketChannel = socketChannel;
	}
	
	@Override
	public void run() {
		// 取得通道对应的Socket
		Socket socket = socketChannel.socket();
		System.out.println("客户端连接:"+socket.getInetAddress()+":"+socket.getPort());
		try {
			//BufferedReader br = getReader(socket);
			//PrintWriter pw = getWriter(socket);
			readLine(socketChannel);
			
//			String msg = null;
//			while((msg=br.readLine())!=null){
//				System.out.println(msg);
//				pw.println(echo(msg));
//				if(msg.equals("88")){
//					break;
//				}
//			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally{
			if(socketChannel!=null){
				try {
					socketChannel.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}

	private String echo(String msg) {
		return "echo:"+msg;
	}

	private BufferedReader getReader(Socket socket) throws Exception{
		// 取得Socket的输入流
		InputStream in = socket.getInputStream();
		return new BufferedReader(new InputStreamReader(in));
	}
	
	private PrintWriter getWriter(Socket socket) throws Exception{
		OutputStream out = socket.getOutputStream();
		return new PrintWriter(out,true);
	}
	
	
	// 采用缓冲区来读取数据,极限、位置等看打印结果就能清楚了
	public String readLine(SocketChannel socketChannel) throws IOException {
		// 创建1024大小的缓冲区
		ByteBuffer buffer = ByteBuffer.allocate(1024);
		// 一个字节的缓冲区
		ByteBuffer temp = ByteBuffer.allocate(1);
		boolean isLine = false;
		boolean isEnd = false;
		String data = null;
		// 当一行没有结束,并且没有到达结尾
		while(!isLine&&!isEnd){
			// 清除缓冲区,极限设为容量
			temp.clear();
			// 阻塞模式,只要读取1个字节或末尾才返回
			// 非阻塞模式,可能返回0
			int n = socketChannel.read(temp);
			if(n==-1){
				isEnd = true;
				break;
			}
			if(n==0){
				continue;
			}
			
			System.out.println("开始位置="+buffer.position());
			System.out.println("开始极限="+buffer.limit());
			System.out.println("开始容量="+buffer.capacity());
			// 极限设为位置,位置设为0
			// 只有极限以内可以读写
			temp.flip();
			// 复制内容到buffer中
			buffer.put(temp);
			System.out.println("写入buffer位置="+buffer.position());
			System.out.println("写入buffer极限="+buffer.limit());
			System.out.println("写入buffer容量="+buffer.capacity());
			
			// 把极限设为写入数据的位置
			buffer.flip();
			System.out.println("flip后位置="+buffer.position());
			System.out.println("flip后极限="+buffer.limit());
			System.out.println("flip后容量="+buffer.capacity());
			Charset charset = Charset.forName("GBK");
			CharBuffer charBuffer = charset.decode(buffer);
			data = charBuffer.toString();
			if(data.indexOf("\r\n")!=-1){
				// 如果有回车换行,一行结束
				isLine = true;
				// 切除回车换行
				data = data.substring(0, data.indexOf("\r\n"));
				break;
			}
			
			System.out.println("位置="+buffer.position());
			System.out.println("极限="+buffer.limit());
			System.out.println("容量="+buffer.capacity());
			// 位置设为极限,为下次读取准备
			buffer.position(buffer.limit());
			// 把极限设为容量
			buffer.limit(buffer.capacity());
		}
		return data;
	}
	
}