Java——NIO流

本文详细介绍了Java NIO,包括NIO概述、NIO与IO的区别、缓冲区(Buffer)和通道(Channel)的概念及其操作,以及文件通道、非阻塞式网络通信和管道(Pipe)。同时,也简述了进程与线程的基础知识,探讨了多进程、多线程的意义以及并行与并发的区别。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、Java NIO
(一)NIO概述
是从 Java 1.4 版本开始引入的一个新的 IO 流,与原来的 IO 有同样的作用和目的,但是使用的方式完全不同, NIO 支持面向缓冲区的、基于通道的 IO 操作。 NIO 将以更加高效的方式进行文件的读写操作。

(二)Java NIO 与 IO 的主要区别

IONIO
面向流(StreamOriented)面向缓冲区(BufferOriented)
阻塞IO(BlockingIO)非阻塞IO(NonBlockingIO)
(无)选择器(Selectors)

(三)缓冲区 (Buffer) 和通道 (Channel)
简而言之, Channel 负责传输, Buffer 负责存储
一个用于特定基本数据类型的容器。由 java.nio 包定义的,所有缓冲区都是 Buffer 抽象类的子类。
由 java.nio.channels 包定义的。 Channel 表示 IO 源与目标打开的连接。
Channel 类似于传统的“流”。只不过 Channel本身不能直接访问数据, Channel 只能与Buffer 进行交互。
Java NIO 中的 Buffer 主要用于与 NIO 通道进行交互,数据是从通道读入缓冲区,从缓冲区写入通道中的。
缓冲区存取数据的两个核心方法:

  • put() : 存入数据到缓冲区中
  • get() : 获取缓冲区中的数据
  • flip(); 切换读取数据模式
  • rewind() : 可重复读
  • clear() : 清空缓冲区. 但是缓冲区中的数据依然存在,但是处于“被遗忘”状态
  • mark() : 标记是一个索引,通过 Buffer 中的 mark() 方法
// Buffer 缓冲区中几个重要的属性
        //capacity; 缓冲区的最大容量
        //position 读取数据是,具体位置
        //limit; 界限
        // mark 书签,标记,可以记录 position 可以通过一个方法,会到我刚才标记的位置
        //容器:有两个方法,可以写入数据和读取数据
        //定义一个缓冲区 容量为10
        String str = "abcde";
        ByteBuffer byteBuffer = ByteBuffer.allocate(10);
        System.out.println("capacity:" + byteBuffer.capacity());
        System.out.println("position:" + byteBuffer.position());
        System.out.println("limit:" + byteBuffer.limit());
        System.out.println("------------put()--------------------");
        byteBuffer.put(str.getBytes());
        System.out.println("capacity:" + byteBuffer.capacity());
        System.out.println("position:" + byteBuffer.position());
        System.out.println("limit:" + byteBuffer.limit());
        //容器中放了数据后,我想读取数据,我们要切换成读取模式
        System.out.println("-------------flip()---------------");
        byteBuffer.flip();
        System.out.println("capacity:" + byteBuffer.capacity());
        System.out.println("position:" + byteBuffer.position());
        System.out.println("limit:" + byteBuffer.limit());
        //接下来我们要读取数据
        System.out.println("-------------get()---------------");
        byte[] bytes = new byte[byteBuffer.limit()];
        byteBuffer.get(bytes);
        System.out.println("capacity:" + byteBuffer.capacity());
        System.out.println("position:" + byteBuffer.position());
        System.out.println("limit:" + byteBuffer.limit());
        System.out.println(new String(bytes));
        //可以重复从缓冲区中读取数据
        System.out.println("-------------rewind()---------------");
        byteBuffer.rewind();
        System.out.println("capacity:" + byteBuffer.capacity());
        System.out.println("position:" + byteBuffer.position());
        System.out.println("limit:" + byteBuffer.limit());

方式1:

 //方式1 通过
        // FileInputStream
        // FileOutputStream
        // RandomAccessFile
        //  中的getChannel()方法可以获取通道
        FileInputStream in = new FileInputStream("领悟.mp3");
        FileOutputStream out = new FileOutputStream("领悟2.mp3");
        //获取通道
        FileChannel inChannel = in.getChannel();
        FileChannel outChannel = out.getChannel();
        //定义缓冲区
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024); //用的是非直接缓冲区
        while (inChannel.read(byteBuffer) != -1) {
            //切换读取模式
            byteBuffer.flip();
            outChannel.write(byteBuffer);
            byteBuffer.clear();//清空缓冲区
        }
        in.close();
        out.close();
        inChannel.close();
        outChannel.close();

(四)文件通道 (FileChannel)
(五)NIO 的非阻塞式网络通信
(六)管道 (Pipe)
(七)Java NIO2

二、进程概述及多进程的意义
A:线程和进程
要想说线程,首先必须得聊聊进程,因为线程是依赖于进程存在的。
B:进程概述
什么是进程呢?通过任务管理器我们就可以看到进程的存在。
概念:进程就是正在运行的程序,是系统进行资源分配和调用的独立单位。 每一个进程都有它自己的内存空间和系统资源。
C:多进程的意义
单进程计算机只能做一件事情。而我们现在的计算机都可以一边玩游戏(游戏进程),一边听音乐(音乐进程),
所以我们常见的操作系统都是多进程操作系统。比如:Windows,Mac和Linux等,能在同一个时间段内执行多个任务。
对于单核计算机来讲,游戏进程和音乐进程是同时运行的吗?不是。
因为CPU在某个时间点上只能做一件事情,计算机是在游戏进程和音乐进程间做着频繁切换,且切换速度很快,
所以,我们感觉游戏和音乐在同时进行,其实并不是同时执行的。多进程的作用不是提高执行速度,而是提高CPU的使用率。
二、线程概述及多线程的意义及并行和并发的区别
A:什么是线程
在一个进程内部又可以执行多个任务,而这每一个任务我们就可以看成是一个线程。是程序使用CPU的基本单位。
B:多线程有什么意义呢?
多线程的作用不是提高执行速度,而是为了提高应用程序的使用率。
那么怎么理解这个问题呢?
我们程序在运行的使用,都是在抢CPU的时间片(执行权),如果是多线程的程序,那么在抢到
CPU的执行权的概率应该比较单线程程序抢到的概率要大.那么也就是说,CPU在多线程程序
中执行的时间要比单线程多,所以就提高了程序的使用率.但是即使是多线程程序,那么他们
中的哪个线程能抢占到CPU的资源呢,这个是不确定的,所以多线程具有随机性.
C:大家注意两个词汇的区别:并行和并发。
前者是逻辑上同时发生,指在某一个时间内同时运行多个程序。
后者是物理上同时发生,指在某一个时间点同时运行多个程序。
三、多线程
(一)Java程序运行原理和JVM的启动是多线程的吗?
A:Java程序运行原理
Java命令会启动java虚拟机,启动JVM,等于启动了一个应用程序,也就是启动了一个进程。
该进程会自动启动一个 “主线程” ,然后主线程去调用某个类的 main 方法。
所以 main方法运行在主线程中。
B:JVM的启动是多线程的吗: JVM启动至少启动了垃圾回收线程和主线程,所以是多线程的。
(二)程序实现的方式1

A:如何实现多线程:
如何实现呢?
	由于线程是依赖进程而存在的,所以我们应该先创建一个进程出来。
  	而进程是由系统创建的,所以我们应该去调用系统功能创建一个进程。
  	但是Java是不能直接调用系统功能的,所以,我们没有办法直接实现多线程程序。
  		但是呢?Java可以去调用C/C++写好的程序来实现多线程程序。
  		由C/C++去调用系统功能创建进程,然后由Java去调用这样的东西,
  		然后提供一些类供我们使用。我们就可以实现多线程程序了。
	参考Thread类
	B:多线程程序实现的方式1
		a:继承Thread类
		b:步骤及代码演示
		c:几个小问题:
			启动线程使用的是那个方法
			线程能不能多次启动
			run()和start()方法的区别
			*我们启动线程使用不是run方法,而应该是start方法.使该线程开始执行;
	 Java 虚拟机调用该线程的 run 方法。  
     为什么要重写run方法?
这个类是一个线程类,那么在这个类中我们可不可以写一些其他的方法呢?		
   *我们可以在写其他的方法,那么其他方法中封装的代码都是需要被我们线程执行的吗? 不一定
   *那么也就是run方法中封装应该是必须被线程执行的代码.
 run方法中的代码的书写原则: 一般是比较耗时的代码
 C:案例演示:	多线程程序实现的方式1

(三)获取和设置线程对象名称

A:Thread类的基本获取和设置方法
	public final String getName()//获取线程名称
	public final void setName(String name)//设置线程名称
	其实通过构造方法也可以给线程起名字
	思考:
		如何获取main方法所在的线程名称呢?
		public static Thread currentThread()//获取当前执行的线程
	 * 我们现在是想获取主线程的名称,那么我们可不可以先获取到主线程,
	 如果我们能获取到主线程,那么我们就可以调用getName方法获取对应的名称.
	 * 如何获取主线程呢? public static Thread currentThread()返回对当前正在执行的线程对象的引用。 
B:案例演示:	获取和设置线程对象名称

(四)线程调度及获取和设置线程优先级

A:线程的执行
	假如我们的计算机只有一个 CPU,那么 CPU 在某一个时刻只能执行一条指令,
	线程只有得到 CPU时间片,也就是使用权,才可以执行指令。那么Java是如何对线程进行调用的呢?
B:线程有两种调度模型:
	分时调度模型   	所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间片
	抢占式调度模型   优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个,
					优先级高的线程获取的 CPU 时间片相对多一些。 
					Java使用的是抢占式调度模型。
C:如何设置和获取线程优先级
	public final int getPriority() //获取线程的优先级
	public final void setPriority(int newPriority)//设置线程的优先级
D:案例演示:	获取和设置线程优先级
注意事项:  有的时候我们给线程设置了指定的优先级,但是该线程并不是按照优先级高的线程执行,那是为什么呢?
- 因为线程的优先级的大小仅仅表示这个线程被CPU执行的概率增大了.但是我们都知道多线程具有随机性,
- 所以有的时候一两次的运行说明不了问题
- * 我们现在呢没有给线程设置优先级,那么java采用的是抢占式调度模型,那么这个线程应该存在一个默认的优先级.
 * 那么这个默认的优先级是多少呢,以及我们如何来获取线程的优先级.
 * 获取线程的优先级:	
 *public final int getPriority()返回线程的优先级。 
 * 线程的默认优先级是5
 * 给线程设置优先级:
 	public final void setPriority(int newPriority)

(五)线程控制之休眠线程

A:线程休眠:	public static void sleep(long millis) 线程休眠
B:案例演示:	线程休眠
public class MyThread extends Thread {
public void run() {
	for(int x = 0 ; x < 100 ; x++){
		System.out.println(getName() + "----" + x);
		
		try {
			// 线程休眠
			Thread.sleep(1000) ;
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}

(六)线程控制之加入线程

A:加入线程:	public final void join()
	意思就是: 等待该线程执行完毕了以后,其他线程才能再次执行
	注意事项: 在线程启动之后,在调用方法
B:案例演示:	加入线程
// 创建三个线程对象
	MyThread mt1 = new MyThread("李渊") ;
	MyThread mt2 = new MyThread("李元霸") ;
	MyThread mt3 = new MyThread("李世民") ;
	// 启动线程
	mt1.start() ;
	 * 我们都只要"李渊"应该是"李元霸"和"李世民"他爹,那么在执行的时候,我们需要先将
	 * 李渊线程执行完毕以后再执行其他的线程,那么怎么完成这个事情呢?
	 * 我们需要使用一个线程的方法:
	 * 		public final void join() 等待该线程执行完毕以后再执行其他线程。 
	mt1.join() ;//线程加入 让李渊线程先执行完 其他两个线程再去执行
	mt2.start() ;
	mt3.start() ;

(七)线程控制之礼让线程
礼让线程: public static void yield(): 暂停当前正在执行的线程对象,并执行其他线程。
(八)线程控制之守护线程

A:守护线程:	public final void setDaemon(boolean on):
	将该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时,Java 虚拟机退出。 
	该方法必须在启动线程前调用。 
B:案例演示:	守护线程
// 创建二个线程对象
	MyThread mt1 = new MyThread("关羽") ;
	MyThread mt2 = new MyThread("张飞") ;
	 * 将mt1和mt2线程标记为守护线程 我们的理想状态是当刘备线程执行完以后 其他两个线程立马死掉
	 但实际情况不是那么理想 其他两个线程还会蹦跶一会
	mt1.setDaemon(true) ;//该方法必须在启动线程前调用。
	mt2.setDaemon(true) ;
	// 启动线程
	mt1.start() ;
	mt2.start() ;
	// 给主线程设置名称
	Thread.currentThread().setName("刘备") ;
	for(int x = 0 ; x < 5 ; x++){
		System.out.println(Thread.currentThread().getName() + "----" + x);
	} 

(九)线程控制之中断线程

A:中断线程
	public final void stop():		停止线程的运行
	public void interrupt():		中断线程(这个翻译不太好),查看API可得当线程调用wait(),sleep(long time)方法的时候处于阻塞状态,可以通过这个方法清除阻塞
B:案例演示:	中断线程
	// 创建对象
	MyThread mt = new MyThread() ;
	// 给线程设置名称
	mt.setName("宋承宪") ;
	// 启动线程
	mt.start() ;
	try {
		Thread.sleep(3000) ;	
		 * 线程控制之线程终止:
		 * 		public final void stop(): 直接将线程停止 干掉了线程
		 *  	public void interrupt():  打断线程的阻塞状态, 它还将收到一个 InterruptedException。 
		//mt.stop() ;
		// public void interrupt()中断线程。 打断阻塞状态 sleep 就是一个阻塞状态
		mt.interrupt() ;
	} catch (InterruptedException e) {
		e.printStackTrace();
	}
	public class MyThread extends Thread {
public void run() {
	for(int x = 0 ; x < 100 ; x++){
		System.out.println("线程开始了......");
		// 线程休眠
		try {
			Thread.sleep(10000) ;
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("线程结束了......");
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值