NIO及Selector多路复用选择器

5种CUP调度算法

     1. FCFS(First come first server )先来先服务调度算法,处理先来的线程,线程处理完后
再处理下一个来的线程。
     2. 时间片轮转调度算法。这个算法的出现,解决了FCFS算法的问题,有效缩短了响应时间,
提高了cpu利用率。但是这个算法可能存在的缺陷是:短任务处于饥饿状态。
     3. 短任务优先调度算法:优先去完成短任务。这个算法的局限性:可能会导致长任务时常处
于饥饿状态。
     4. 优先级调度算法:为线程分别分配优先级,优先级高的线程优先被处理。局限性就是优先
级低的线程长期处于饥饿状态。
     5. 混合调度算法:这个算法把之前算法的优点进行结合,然后做线程调度

PS:饥饿状态是指争抢CUP资源的时间比该程序执行完毕的时间还长,例如:该进程只需要执行0.5秒,但是经过了5秒才抢到资源进行运行。

NIO和BIO对比

NIO(non blocking I/O)非阻塞I/O,jdk1.4引入的新I/O
BIO,即阻塞I/O;

  1. BIO
    • 面向流的:
      • InputStream(),OuputStream
        字节输入流,字节输出流
        Reader,Writer
        字符输入流,字符输出流
    • 特点:
      • 流是有方向性的、连续不断的.
      • 总结局限性:不能灵活的操作流里的数据
  2. NIO
    • 面向缓冲区的:
      • NIO是通过缓冲区去操作数据的,可以用缓冲区灵活操作数据。
      • NIO是面向通道的(Channel),在通道上,即可以读数据,也可以写数据。
      • 总结:通道相当于提供了运算环境,缓冲区是运输数据的载体。

ByteBuffer

这个类十分重要,这是NIO的缓冲区。
ByteBuffer缓冲区类,有三个重要的属性
     *capacity  10:容量,该缓冲区可以最多保存10个字节
     * position 0:表示位置
     * limit 10:限制位(用在获取元素时限制获取的边界)

方法如下:

/*创建 allocate(10) 表示最大存10个字节*/
ByteBuffer buf=ByteBuffer.allocate(10);
/*创建 内容为传入的内容。*/
ByteBuffer.wrap("HelloWorld".getBytes());

/*
    put(byte bt)向缓存区添加一个字节
    每调用一次该方法position的值会加一。
*/
buf.put(1);
/*
    get()获取position指定位置的一个字节内容。
    每调用一次该方法,position++;
    如果在调用get()时,position>=limit,
    则抛出异常BufferUnderflowException
*/
buf.get();
/*
    position(int pt):设置position的值为pt
*/
buf.position(0);
/*
    position():获取当前缓冲区的position属性的值
*/
int i = buf.position();
/*
    limit(int):设置限制为的值
*/
buf.limit(10);
/*
    limit():获取当前缓冲区的limit属性的值。
*/  
int i = buf.limit();
/*
    flip()方法:反转缓存区,一般用在添加完数据后。limit = position;将limit的值设置为当前position的值,再将position的值设置为0
*/
buf.flip();
/*
    clear():"清除缓存区",是以覆盖的方式达到清除的目的,写完数据后,flip()一下可以防止读取垃圾数据。
*/
buf.clear();
/*
    hasRemaining()判断缓冲区中是否还有有效的数据,有返回true,没有返回false
*/
buf.hasRemaining();

这个类在nio的read(),write()作为参数传入;

会阻塞的方法

ServerSocket中 共有以下几个方法会阻塞。

  1. accept()
    • 直到服务器获得到连接后,阻塞才释放。
  2. connect()
    • 指定连接成功,阻塞才释放。
  3. read()
    • 直到读取到内容后,阻塞才被释放。
  4. write()
    • 向网卡设备缓冲区中写数据,写满后会阻塞到有程序将网卡缓冲区中的数据取走。

NIO中ServerSocketChannel / SocketChannel 提供了两种模式,阻塞/非阻塞,默认是阻塞的
需要用该方法来设置:configureBlocking(false); false为非阻塞,true为阻塞。

NIO_Socket实现

包括多路复用选择器,实现单个线程为多个用户操作。
这种情况适用于大量短请求的情况。
会监听四个事件 accept connect read write

客户端代码如下:

public class NIOServer {
    public static void main(String[] args) throws IOException {
        ServerSocketChannel ssc = ServerSocketChannel.open();
        /*设置非阻塞*/
        ssc.configureBlocking(false);
        ssc.bind(new InetSocketAddress(6666));
        /*定义多路复用选择器*/
        Selector sel = Selector.open();
        /*注册事件accept*/
        ssc.register(sel, SelectionKey.OP_ACCEPT);
        while(true){
            /*select()在没有收到相关事件时,产生阻塞,直到有事件触发,阻塞才放开*/
            sel.select();
            /*获取所有的请求事件*/
            Set sks = sel.selectedKeys();
            Iterator iter = sks.iterator();
            /*遍历所有请求*/
            while(iter.hasNext()){
                SelectionKey sk = iter.next();
                /*不同请求不同的处理方式。连接事件*/
                if(sk.isAcceptable()){
                    /*获取连接对象,因为这会还没有连接,所以通过ServerSocketChannel获取*/
                    ServerSocketChannel ssc1 = (ServerSocketChannel) sk.channel();

                    SocketChannel sc = ssc1.accept();
                    //因为是非阻塞,要处理一下否则没有连接则会空指针
                    while(sc==null){
                        sc=ssc1.accept();
                    }
                    sc.configureBlocking(false);
                    /*为sc注册read和write事件*/
                    sc.register(sel, SelectionKey.OP_READ|SelectionKey.OP_WRITE);
                    System.out.println("提供服务的线程id:"+Thread.currentThread().getId());
                }
                //写出事件
                if(sk.isWritable()){
                    /*获取连接对象*/
                    SocketChannel sc = (SocketChannel) sk.channel();
                    ByteBuffer buf = ByteBuffer.wrap("get".getBytes());
                    /*去掉写事件*/
                    sc.register(sel, sk.interestOps()&~SelectionKey.OP_WRITE);
                }
                //读入事件
                if(sk.isReadable()){
                    /*获取连接对象*/
                    SocketChannel sc =  (SocketChannel) sk.channel();
                    ByteBuffer buf = ByteBuffer.allocate(10);
                    sc.read(buf);
                    System.out.println("服务器收到:"+new String (buf.array()));
                    /*注销读事件*/
                    /*0000 0101 sk.interestOps()获取原事件
                     *1111 1110  !OP_READ
                     *0000 0100 OP_WRITE
                     */
                    sc.register(sel, sk.interestOps()&~SelectionKey.OP_READ);
                }
                /*获取完毕后,将事件删除*/
                iter.remove();
            }
        }


    }
}

客户端代码如下:

public class NIOClient {
    public static void main(String[] args) throws Exception {
        SocketChannel sc = SocketChannel.open();
        //关闭阻塞
        sc.configureBlocking(false);
        sc.connect(new InetSocketAddress("127.0.0.1", 6666));
        //sc是否连接
        while(!sc.isConnected()){
            //没有链接就一直到连接为止
            sc.finishConnect();
        }
        System.out.println("客户端有连接连入");
        ByteBuffer buf = ByteBuffer.wrap(
                "helloworld".getBytes());
        sc.write(buf);
        System.out.println("客户端信息已经写出");
        ByteBuffer readBuf = ByteBuffer.allocate(3);
        sc.read(readBuf);
        System.out.println("客户端读到服务器端传递过来的信息:"
              +new String(readBuf.array()));
        while(true);
    }
}

你可能感兴趣的:(JAVA)