单例模式
要求:当前类有且只有一个对象,一旦当前类存在一个对象之后,其他类无法在当前类创建其他对象。就算要创建,代码返回的对象依然式上一次创建的对象
单例模式推导【懒汉】
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