首先了解
同步 : 使用同步IO时 java自己处理IO读写(自己亲自去银行取钱)
异步 : 使用异步IO时 java将IO读写委托给OS处理 需要将数据缓冲区地址和大小传输给IO(银行卡的账户和密码)OS需要支持异 步IO操作的API(自己委托朋友去银行取钱然后转交给你)
阻塞 :使用IO阻塞时 ,java调用会一直阻塞到读写完成完成才返回(ATM排队取款 你只能等待)
非阻塞 :使用非阻塞IO时 ,如果不能读写java调用会立即返回,当IO事件分发器通知可再次读写时进行读写,不断循环直到读写 完成(柜台取款 取号 有号可取 没念到号时不可以取钱 可以在等候区做其他事情)
传统的IO操作的问题?
- 没有数据缓冲区
- IO操作会被阻塞 扩展能力受限制
- 所支持的字符集编码有限
- 不支持正则表达式 数据处理困难
NIO
什么是NIO?
- NIO是同步非阻塞
- NIO 是( NEW IO ) java非阻塞式IO 提升了IO 不可以多线程执行的缺点
- NIO 是基于块的(block)以块为基本单位处理数据
- 为所有原始类型提供(buffer)缓存功能
- 增加通道(channel)对象 做为新的原始IO对象
- 支持锁和内存映射文件的文件访问接口
- 服务器实现模式为一个请求一个线程,即为客户端发送的连接请求都会注册到多路复用器上,多用复用器在轮询到有连接IO请求时才启动一个线程进行处理。
NIO有什么用处?
- 完全取代了 java.io.File 与文件系统的交互
- 提供了新的异步处理类 无需手动配置线程池和其他底层并发控制 便可在后台中执行文件和网络IO操作
- 引入新的Network-Channel构造方法,简化了套接字Socket与通道的编码关系
JAVA NIO由以下核心组件组成:Channels、Buffers、Selectors
-
Buffer(缓冲区)介绍:
- Java NIO Buffers用于和NIO Channel交互。 我们从Channel中读取数据到buffers里,从Buffer把数据写入到Channels;
- Buffer本质上就是一块内存区;
- 一个Buffer有三个属性是必须掌握的,分别是:capacity容量、position位置、limit限制。
-
Buffer的常见方法
- Buffer clear()
- Buffer flip()
- Buffer rewind()
- Buffer position(int newPosition)
-
Buffer的使用方式/方法介绍:
-
分配缓冲区(Allocating a Buffer):
ByteBuffer buf = ByteBuffer.allocate(28);//以ByteBuffer为例子
-
写入数据到缓冲区(Writing Data to a Buffer)
写数据到Buffer有两种方法:
1.从Channel中写数据到Buffer
int bytesRead = inChannel.read(buf); //read into buffer.
2.通过put写数据:
buf.put(127);
-
-
Channel(通道)介绍
- 通常来说NIO中的所有IO都是从 Channel(通道) 开始的。
- NIO Channel通道和流的区别:
- FileChannel的使用
- SocketChannel和ServerSocketChannel的使用
- ️DatagramChannel的使用
-
Scatter / Gather
- Scatter: 从一个Channel读取的信息分散到N个缓冲区中(Buufer).
- Gather: 将N个Buffer里面内容按照顺序发送到一个Channel.
-
通道之间的数据传输
- 在Java NIO中如果一个channel是FileChannel类型的,那么他可以直接把数据传输到另一个channel。
- transferFrom() :transferFrom方法把数据从通道源传输到FileChannel
- transferTo() :transferTo方法把FileChannel数据传输到另一个channel
-
Selector(选择器)介绍
- Selector 一般称 为选择器 ,当然你也可以翻译为 多路复用器 。它是Java NIO核心组件中的一个,用于检查一个或多个NIO Channel(通道)的状态是否处于可读、可写。如此可以实现单线程管理多个channels,也就是可以管理多个网络链接。
- 使用Selector的好处在于: 使用更少的线程来就可以来处理通道了, 相比使用多个线程,避免了线程上下文切换带来的开销。
-
Selector(选择器)的使用方法介绍
- Selector的创建
Selector selector = Selector.open();
- 注册Channel到Selector(Channel必须是非阻塞的)
channel.configureBlocking(false); SelectionKey key = channel.register(selector, Selectionkey.OP_READ);
- SelectionKey介绍
一个SelectionKey键表示了一个特定的通道对象和一个特定的选择器对象之间的注册关系。
- 从Selector中选择channel(Selecting Channels via a Selector)
选择器维护注册过的通道的集合,并且这种注册关系都被封装在SelectionKey当中.
- 停止选择的方法
wakeup()方法 和close()方法。
- 模板代码
有了模板代码我们在编写程序时,大多数时间都是在模板代码中添加相应的业务代码。
- 客户端与服务端简单交互实例
NIO中的文件系统的操作
一 文件I/O基石:Path:
- 创建一个Path
- File和Path之间的转换,File和URI之间的转换
- 获取Path的相关信息
- 移除Path中的冗余项
二 拥抱Files类:
- Files.exists() 检测文件路径是否存在
- Files.createFile() 创建文件
- Files.createDirectories()和Files.createDirectory()创建文件夹
- Files.delete()方法 可以删除一个文件或目录
- Files.copy()方法可以吧一个文件从一个地址复制到另一个位置
- 获取文件属性
- 遍历一个文件夹
- Files.walkFileTree()遍历整个目录
主要由File类提供
File类:轻松赋值 移动 删除 处理文件的工具类
移动(原子移动)
//只能同盘移动
public void moveFile() throws IOException {
Path p = Paths.get("D:\\J2SECS\\1.txt");
Path p1 = Paths.get("D:\\J2SECS\\测试文件夹1\\1.txt");
Files.move(p , p1 , StandardCopyOption.REPLACE_EXISTING , StandardCopyOption.ATOMIC_MOVE) ;
}
}
拷贝
public void copyFile() throws IOException {
Path p = Paths.get("D:\\J2SECS\\2.txt");
Path p1 = Paths.get("D:\\J2SECS\\测试文件夹1\\2.txt");
Files.copy(p,p1, StandardCopyOption.REPLACE_EXISTING,StandardCopyOption.COPY_ATTRIBUTES);
}
删除
public void delFile()
{
Path target = Paths.get("D:\\J2SECS\\测试文件夹1\\1.txt");
try {
Files.delete(target);
} catch (IOException e) {
e.printStackTrace();
}
}
创建(只能创建文件)
public void createFile()
{
Path target = Paths.get("D:\\J2SECS\\测试文件夹1\\1.txt");
try {
Files.createFile(target);
} catch (IOException e) {
e.printStackTrace();
}
}
创建别的不能调用createFile 要调用其他的
创建目录
public void createDir(){
Path target=Paths.get("c://mk");
try {
Files.createDirectory(target);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}//创建文件或目录
}
设置只读 隐藏等
public void setAttribute(){
Path path=Paths.get("c://1.sql");
DosFileAttributeView view=Files.getFileAttributeView(path,DosFileAttributeView.class);
try {
view.setReadOnly(false);
view.setHidden(false);
} catch (IOException e) {
e.printStackTrace();
}
读取文件
//读取文件属性
@Test
public void getAttr(){
Path path = Paths.get("c://1.sql");
DosFileAttributeView view = Files.getFileAttributeView(path, DosFileAttributeView.class);
if (view != null) {
DosFileAttributes attrs=null;
try {
attrs = view.readAttributes();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(attrs.isHidden());
}
}
查看文件属性
public void fileAttribute() {
try {
Path zip = Paths.get("c://1.sql");
System.out.println(zip.toAbsolutePath().toString());
System.out.println(Files.getLastModifiedTime(zip));
System.out.println(Files.size(zip));
System.out.println(Files.isSymbolicLink(zip));
System.out.println(Files.isDirectory(zip));
System.out.println(Files.readAttributes(zip, "*"));
} catch (IOException ex) {
System.out.println("Exception" + ex.getMessage());
}
}
路径 创建的修改时间 大小 是否有快捷 是否是目录 输出所有属性
BIO
- BIO 也称(BLOCK IO)同步并阻塞
- 服务器实现模式为一个连接一个线程,即为客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情就会造成不必要的线程开销,不过可以通过线程池来改善
- BIO方法适用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中
AIO
- AIO 也称 NIO2 是异步非阻塞
- 服务器模式为一个有效请求一个线程 客户端的IO请求都是由OS先完成了再通知服务器应用去启动线程进行处理
- AIO用于连接数目多且连接较长的(重操作)构架,比如相册服务器,充分调用OS参与并发操作
监听器
//监听器
public class J34 {
public static void main(String[] args) throws IOException, InterruptedException {
//1.创建监听器
WatchService ws = FileSystems.getDefault().newWatchService();
//2.哪个文件被监听
Path p = FileSystems.getDefault().getPath("D:\\J2SECS");
//3.监听什么动作时候监听器被触发
WatchKey key = p.register(ws,ENTRY_MODIFY);
boolean flag = true;
while (flag)
{
key = ws.take();
for(WatchEvent event : key.pollEvents() )
{
if(event.kind()==ENTRY_MODIFY)
{
System.out.println("文件发生变化了");
}
}
key.reset();//复位
flag = false;
}
}
}
Path类
- Path:类的方法可以用来获取路径信息访问该路径的各种元素将路径转换为其他形式 或者提取路径的一部分
- Paths:提供返回路径的一个辅助方法
- 可以从Path对象中获得许多目录信息
public static void main(String[] args) {
// Path p = Paths.get("c.txt");
// System.out.println(p.getFileName());
Path p= Paths.get("/use/bin/zip/po/");
System.out.println(p.getFileName());
System.out.println("获取名称元素的数量"+p.getNameCount());
System.out.println("父路径:"+p.getParent());
System.out.println("父路径:"+p.getRoot());
System.out.println(p.subpath(0,1));
System.out.println(p.subpath(0,2));
Path normalizedPath=Paths.get("Listing_2_1.java").normalize();//去掉冗余空格
System.out.println(normalizedPath);
try {
Path realPath=Paths.get("1.txt").toRealPath();
System.out.println(realPath);
} catch (IOException e) {
e.printStackTrace();
}
Path path3=Paths.get("/uat/weikun");//
Path path4=Paths.get("ok.properties");
System.out.println(path3.resolve(path4));
}
}
处理目录与目录树
public class J28 {
public static void main(String[] args) {
Path p = Paths.get("D:\\J2SECS");
try {
Files.walkFileTree( p , new SimpleFileVisitor<Path>()
{
@Override
public FileVisitResult visitFile (Path file , BasicFileAttributes attrs)throws IOException
{
if(file.getFileName().toString().endsWith("txt"))
{
System.out.println(file.getFileName());
}
return FileVisitResult.CONTINUE;
// return super.visitFile(file , attrs);
}
}
);
}catch (IOException e)
{
e.printStackTrace();
}
}
}
找到当前指定路径下 所有的指定格式文件
读sql文件
public void simpleFileRead()
{
Path p = Paths.get("D:\\J2SECS\\1.sql");
try {
List<String> list = Files.readAllLines(p, StandardCharsets.UTF_8);
for (String s : list)
{
System.out.println(s);
}
} catch (IOException e) {
e.printStackTrace();
}
}
写入文件(需要先创建才能写)
@Test
public void simpleFileWrite() throws IOException {
Path p = Paths.get("D:\\J2SECS\\9.sql");
if(!Files.exists(p))
{
Files.createFile(p);
}
try( BufferedWriter bw=Files.newBufferedWriter(p, StandardCharsets.UTF_8, StandardOpenOption.APPEND))
{
bw.write("HELLO WORLD");
}catch (IOException e)
{
e.printStackTrace();
}
}