一、Java NIO
(一)NIO概述
是从 Java 1.4 版本开始引入的一个新的 IO 流,与原来的 IO 有同样的作用和目的,但是使用的方式完全不同, NIO 支持面向缓冲区的、基于通道的 IO 操作。 NIO 将以更加高效的方式进行文件的读写操作。
(二)Java NIO 与 IO 的主要区别
IO | NIO |
---|---|
面向流(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("线程结束了......");
}
}