Java之单例模式&&NIO

单例模式

要求:当前类有且只有一个对象,一旦当前类存在一个对象之后,其他类无法在当前类创建其他对象。就算要创建,代码返回的对象依然式上一次创建的对象

单例模式推导【懒汉】

package com.wcc.c_single;

/**
 * SingleDog要求是一个单例类,整个代码运行周期内有且只有一个对象
 * @Author kk
 * @Date 2020/3/13 10:54
 */
public class SingleDog {
    private static SingleDog sd = null;
    private SingleDog(){}

    //同步方法
    public static synchronized SingleDog getInstance(){
        if (null == sd){
            sd = new SingleDog();
        }
        return sd;

    }
}

另一种单例模式【饿汉】

package com.wcc.c_single;

/**
 * 另一种单例模式
 * @Author kk
 * @Date 2020/3/13 19:30
 */
public class SingleCat {
    private static final SingleCat sc = new SingleCat();
    private SingleCat(){}

    public static SingleCat getInstance() {
        return sc;
    }
}

 

NIO

BIO和NIO

BIO:

BIO ==> BASIC IO(基本 IO), Block IO(阻塞IO)
例如:
    Scanner操作,文件读写操作,Scoket数据传输操作...都是BIO

比如TPC群聊,私聊聊天室
    Socket涉及到的IO,也是BIO
会有资源浪费现象:
    1.多线程,每一个Socket会对应一个线程,如果用户量巨大,会导致线程过多,资源处理过多
    2.采用阻塞状态,一旦进入阻塞状态,代码无法执行其他操作
    3.承载量一般,吞吐量比较小,同时可靠性不佳

NIO:

NIO ==> New IO(新IO), Non-Block IO(非阻塞IO)
非阻塞IO,允许当前程序在处理IO事务时,不会影响其他程序的运行,可以在不使用多线程的情况下,满足IO操作要求。

三大核心部分
    通道:Channel,文件操作,网络数据传递操作使用的通道
    缓冲:Buffer,缓冲使用可以提供操作效率,减少不必要的读写次数
    选择器:Selector,真·核心

Buffer Channel完成文件操作

常用API:
java.nio.Buffer
Buffer缓冲区有:
    ByteBuffer 字节缓冲
    ShortBuffer
    IntBuffer
    CharBuffer
    FloatBuffer
    DoubleBuffer
常用方法:
    public static Byte allocate(int capacity);
        按照指定的字节数分配对应的缓冲区空间,保存字节数据
    public byte get();
        从字节缓冲区对象中读取一个byte类型数组
    public final Buffer flip();
        翻转缓冲区,回到缓冲区的开始位置
    public static ByteBuffer wrap(byte[] arr);
        存入一个byte类型数组到缓冲区,会得到一个新的ByteBuffer
    public static ByteBuffer put(byte[] b);
        将字节数组存入缓冲区
        
Channel接口,通道接口
    FileChannel 文件操作通道
    DatagramChannel UDP协议数据包操作的Channel
    ServerSocketChannel TCP服务端ServerSocket对应Channel
    SocketChannel TCP客户端Socket对应Channel
首先操作文件,以FileChannel为例
    public long read(ByteBuffer buffer);
        从通道中读取数据到ByteBuffer中
    public long write(ByteBuffer buffer);
        从Buffer中写数据到通道中
    public long transferFrom(ReadableByteChannel src, long position, long count);
        从指定srcChannel中,指定位置position开始,读取count个元素,到当前通道中;
        文件复制操作
    public long transferTo(long position, long count, WritableByteChannel target);
        将当前通道中的数据写入target中,从当前通道的position开始,计数count

写入文件数据:

package com.wcc.d_niofile;

import org.junit.Test;

import java.awt.*;
import java.io.*;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

/**
 * @Author kk
 * @Date 2020/3/13 15:17
 */
public class FileNioTest {
    @Test
    public void testNioFileWrite() throws IOException {
        //1.文件操作字节输出流对象
        FileOutputStream fos = new FileOutputStream("F:/Idea/1.txt");

        //2.利用文件操作输出字节流对象获取对应的Channel通道
        FileChannel foc = fos.getChannel();

        //3.准备一个缓冲区
        ByteBuffer buffer = ByteBuffer.allocate(1024 * 4);

        //4.准备数据放入缓冲区
        String str = "测试NIO";
        buffer.put(str.getBytes());

        //5.存在缓冲区数据放入之后,缓冲区指针发生改变 ,到达存入数据的末尾
        //如果此时调用写入操作,会从存入缓冲区之后开始保存
        //让缓冲区指针回到最初的起点,并且操作写入程序,只会保存缓冲区内的有效数据
        buffer.flip();

        //6.缓冲区数据写入到通道中
        foc.write(buffer);

        //6.关闭资源
        fos.close();
    }
    @Test
    public void testNioFileRead() throws IOException {
        //1.文件字节操作输入流
        FileInputStream fis = new FileInputStream("F:/Idea/1.txt");
        //2.FileChannel
        FileChannel fic = fis.getChannel();
        //3.准备缓冲
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        //4.从Channel中读取数据保存到缓冲区当中
        int read = fic.read(buffer);
        System.out.println(read);
        //5.展示数据
        System.out.println(new String(buffer.array() , 0, read));
        //6.关闭资源
        fis.close();
    }
    @Test
    public void testCopyFile(){
        long start = System.currentTimeMillis();
        //1.安排输出流和输入流

        long end = System.currentTimeMillis();

        System.out.println();

    }
}

网络编程使用NIO【重点】

Selector选择器

Selector
    选择器,网络编程使用NIO的大哥
    服务器可以执行一个线程,运行Selector程序,进行监听操作
    新连接,已经连接,读取数据,写入数据  
Selector内的常用方法
    public static Selector Open();
        得到一个选择器对象
    public int select(long timeout);
        监听所有的注册通道,存在IO流操作时会将对应的信息Selection存入到内部的集合中
        参数是一个超时时间
    public Set<Selection Key> selectionKeys();
        返回当前Selector内部集合中保存的所有SelectionKey




SelectionKey

SelectionKey:表示Selector和网络通道之间的注册关系
    拥有四个属性:
        int OP_ACCEPT;  16 需要连接
        int OP_CONNECT;  8  已经连接
        int OP_READ;  4 读取操作
        int OP_WRITE; 1 写入操作
常用方法:
    public abstract Selector selector();
       返回当前SelectionKey保存的对应的Selector
    public abstract SelectableChannel channel();
        获取当前SelectionKey对应的通道
    public boolean isAcceptable();
        是否连接状态
    public boolean isReadable();
        是否可以读
    public boolean isWritable();
        是否可以写
    SelectionKey intersetOps(int ops);
        设置当前SelectionKey需要监听哪些操作
    Object attachment();
        返回当前连接的一些属性

SeverSocketChannel

服务端Socket程序对应的Channel通道
常用方法:
    public static ServerSocketChannel open();
        开启服务器ServerSocketChannel通道,等于开启服务器程序
    public SocketChannel accept();
        该方法非阻塞;获取一个客户端连接,并且得到对应的操作通道
    public fianl SelectionKey register(Selector sel, int ops);
        重点方法;注册当前选择器,并且选择监听什么事件

SocketChannel

客户端Socket对应的Channel对象
常用方法:
    public static SocketChannel open();
        打开一个SocketChannel客户端Channel对象
    public final SelectableChannel configureBlocking(boolean block);
        这里可以设置是否是阻塞状态
        false表示非阻塞
    public boolean connect(SocketAddress remote);
        连接服务器操作
    public boolean finishConnect();
        如果connect方法连接失败,可以通过finishConnect继续连接
    public int write(ByteBuffer buf);
        写入数据到缓冲流中
    public int read(ByteBuffer buf);
        从缓冲流中读取数据
    public final SelectionKey register(Selector sel, int ops, Object attechment);
        注册当前SocketChannel,选择对应的监听操作,并且可以带有Object attechment参数
    public void close();
        关闭SocketChannel
发布了24 篇原创文章 · 获赞 34 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/weixin_42597414/article/details/104836126