一、线程
程序运行阶段不同的运行路线
Thread 线程类
1、自定义线程 继承 Thread
class ThreadA extends Thread{
//重写run方法,定义线程要执行的任务
@Override
public void run(){
for (int i = 0; i <= 20; i++) {
System.out.println(i+this.getName());
//System.out.println(i+Thread.currentThread().getName());
}
}
}
获得线程名:有线程对象时用this.getName()、没对象时用Thread.currentThread().getName()
2、实例化线程对象
Thread a=new ThreadA();
Thread b=new ThreadA();
3、开启线程
a.start();
b.start();//同时进行
//只是普通对象调用方法 一个执行完另一个执行
a.run();
b.run();
4、线程常用的方法
(1)休眠的方法 sleep
public class EasyThreadB {
public static void threadSleep() throws InterruptedException{
//sleep是一个Thread类的静态方法
System.out.println("1-----------");
//让运行到该行代码的线程休眠5s
//休眠后会自动启动线程
Thread.sleep(5000);
System.out.println("2-------------");
}
public static void threadBSleep(){
Thread t=new ThreadB();
t.start();
}
public static void main(String[] args) throws Exception{
//threadSleep();
//threadBSleep();
}
}
class ThreadB extends Thread{
@Override
public void run(){
for (int i = 0; i <= 20; i++) {
if (i%8==0){
try{
Thread.sleep(2000);
}catch (InterruptedException e){
e.printStackTrace();
}
}
System.out.println(i+this.getName());
}
}
}
(2)获取当前线程对象 Thread.currentThread()
public class EasyThreadB {
public static void current(){
System.out.println(Thread.currentThread().getName());
}
public static void main(String[] args) throws Exception{
current();
}
}
(3)设置优先级 setPriority()
public class EasyThreadB {
public static void priority() {
Thread a = new ThreadB();
Thread b = new ThreadB();
//设置优先级
a.setPriority(4);
b.setPriority(6);
//优先级越高,获取CPU资源的几率越大
//优先级 1-10 默认是5 设置其他值报错
a.start();
b.start();
}
public static void main(String[] args) throws Exception{
priority();
}
}
class ThreadB extends Thread{
@Override
public void run(){
for (int i = 0; i <= 20; i++) {
if (i%8==0){
try{
Thread.sleep(2000);
}catch (InterruptedException e){
e.printStackTrace();
}
}
System.out.println(i+this.getName());
}
}
}
※优先级越高,获取CPU资源的几率越大
优先级 1-10 默认是5 设置其他值报错
(4)礼让 yeild
作用:让出CPU资源,让CPU重新分配 防止一条线程长时间占用CPU资源,达到CPU资源合理分配的效果 sleep(0)也可以达到该效果
public class EasyThreadB {
public static void threadYeild(){
Thread a=new ThreadC();
Thread b=new ThreadC();
a.start();
b.start();
}
public static void main(String[] args) throws Exception{
threadYeild();
}
}
class ThreadC extends Thread{
@Override
public void run() {
for (int i = 0; i <= 20; i++) {
if(i%3==0){
System.out.println(this.getName()+"-----执行了礼让方法");
Thread.yield();
}
System.out.println(i+this.getName());
}
}
}
(5)join() 成员方法 加入(插队)
在A线程中执行了B.join() B线程运行完成后,A线程再运行
public class EasyThreadB {
public static void threadJoin(){
Thread a=new ThreadD();
Thread b=new ThreadD(a);
a.start();
b.start();
}
public static void main(String[] args) throws Exception{
threadJoin();
}
}
class ThreadD extends Thread{
private Thread t;
public ThreadD(Thread t){
this.t=t;
}
public ThreadD(){}
@Override
public void run() {
for (int i = 0; i <= 20; i++) {
if(i==10&&t!=null&&t.isAlive()){
System.out.println(this.getName()+"-----执行了JOIN");
try {
t.join();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
System.out.println(i+this.getName());
}
}
}
(6)关闭线程
关闭线程的方法: 1 执行stop方法 不推荐 2 调用interrupt()设置中断状态,这个线程先不中断,我们需要在线程内部判断,中断状态是否被 设置,然后执行中断操作 3 自定义一个状态属性,在线程外部设置此属性,影响线程内部的运行
执行stop方法 不推荐
public class EasyThreadC {
public static void threadStop(){
Thread a=new ThreadE();
a.start();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
a.stop();
}
public static void main(String[] args) {
threadStop();
}
}
class ThreadE extends Thread{
@Override
public void run(){
for (int i = 0; i < 100; i++) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(i);
}
}
}
调用interrupt()设置中断状态 Thread.currentThread().isInterrupted()获得当前中断状态
interrupt()工作要点
如果当前线程实例在调用Object类的wait(),wait(long)或wait(long,int)方法或join(),join(long),join(long,int)方法,或者在该实例中调用了Thread.sleep(long)或Thread.sleep(long,int)方法,并且正在阻塞状态中时,则其中断状态将被清除,并将收到InterruptedException。
如果此线程在InterruptibleChannel上的I / O操作中处于被阻塞状态,则该channel将被关闭,该线程的中断状态将被设置为true,并且该线程将收到java.nio.channels.ClosedByInterruptException异常。
如果此线程在java.nio.channels.Selector中处于被被阻塞状态,则将设置该线程的中断状态为true,并且它将立即从select操作中返回。
如果上面的情况都不成立,则设置中断状态为true。
public class EasyThreadC {
public static void threadInterrupted(){
Thread a=new ThreadF();
a.start();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
a.interrupt();
}
public static void main(String[] args) {
threadInterrupted();
}
}
class ThreadF extends Thread{
@Override
public void run(){
for (int i = 0; i < 100; i++) {
if (Thread.currentThread().isInterrupted()){
break;
}
try {
Thread.sleep(500);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(i);
}
}
}
自定义一个状态属性,在线程外部设置此属性,影响线程内部的运行
public class EasyThreadC {
public static void stopThread(){
ThreadG a=new ThreadG();
a.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
a.stop=true;
System.out.println("设置关闭");
}
public static void main(String[] args) {
stopThread();
}
}
class ThreadG extends Thread{
volatile boolean stop=false;
@Override
public void run() {
while(!stop){
//System.out.println("A");
}
}
}
5、volatile关键词
volatile:不稳定的,易变的
作用:
用以声明变量的值可能随时会被别的线程修改,使用volatile修饰的变量会强制将修改的值立即写入主存,主存中值的更新会使缓存中的值失效(非volatile变量不具备这样的特性,非volatile变量的值会被缓存,线程A更新了这个值,线程B读取这个变量的值时可能读到的并不是是线程A更新后的值)。volatile会禁止指令重排。
当一个变量被volatile
关键字修饰后,一个线程修改了自己工作内存中的该共享变量后,会立即被更新到主内存中,同时其他线程的工作内存存储的该变量副本变为失效状态,当其他线程再次读取或者修改该共享变量时,会直接从主内存中读取最新的值。
6、进程与线程的区别与联系
进程是操作系统资源分配的基本单位,线程是操作系统调度运行的基本单位
(1)进程
进程(process)/任务(task):一个运行起来的程序就是进程;
进程的定义: (1)进程是程序的一次执行;
(2)进程是一个程序及其数据在处理机上顺序执行时所发生的活动;
(3)进程是具有独立功能的程序在一个数据集合上运行的过程,他是系统进行
资源分配和调度的一个独立单位
进程的特征:
(1)动态性--进程的实质是进程实体的执行过程,动态性是进程最基本的特征“进程由创建而
产生,由调度而执行,由撤消而消亡”(2)并发性--是指多个进程实体同存于内存中,且能在一段时间内同时运行
(3)独立性--一个进程无法直接干预另一个进程的内存内容每个进程有自己独立的地址空
间,大大提升了操作系统的稳定性(4)异步性--按各自独立的、不可预知的速度向前推进
进程的三种状态:
(1)就绪状态--等处理机
(2)执行状态--用处理机
(3)阻塞状态--等事件
进程是比较“重量的”--主要体现在资源分配上,资源分配往往是一个耗时操作
系统要给进程分配一个内存(1)系统就需要遍历自己的空间内存的表(数据结构)找到一个大小差不多的空间进行分配(2)很多进程都在问系统申请资源,系统进行资源分配的时候,得一个一个来
(2)线程
线程则是更轻量级的的进程(轻量级进程)一个进程中可以包含多个线程,此时这多个中每个线程都是一个独立可以调度执行的“执行流”(这些执行之间本身就是并发的)
同时这些线程共用同一份进程的系统资源:
①内存空间--操作系统对内存资源的分配采用的是空间模式,不同进程使用内存中的不同区域互相之间不会干扰
②文件描述符表--文件:比如硬盘上存储的数据,往往就是以文件为单位进行整理,进程每次打开一个文件,就会产生一个文件标识符,标识了这个被打开的文件,一个进程可能会打开很多文件,对应了一组文件描述符,把这些文件描述符放到一个顺序表里,就构成文件描述符表
只有在进程启动,创建第一个线程的时候,需要花成本去申请资源,一旦进程(第一个线程)创建完毕,此时,后续再创建的线程,就不必在申请资源了,在这样的情况下,创建和销毁的效率就提高了不少,同一个程序,内部想要并发的完成多组任务,此时使用多线程会更高效,更节约资源。
(3)总结
二、线程生命周期
新建:当一个Thread类或其子类的对象被声明并创建时,新的线程对象处于新建状态。
就绪:处于新建状态的线程被start()后,将进入线程队列等待CPU时间片,此时它已具备了
运行的条件,只是没分配到CPU资源。运行:当就绪的线程被调度并获得CPU资源时,便进入运行状态,run()方法定义了线程的操
作和功能。阻塞:在某种特殊情况下,被认为挂起或执行输入输出操作时,让出CPU并临时中止自己的
执行,进入阻塞状态。终止:线程完成了它的全部工作或线程被提前强制性的中止或出现异常倒置导致结束。
start() : 启动当前线程, 调用当前线程的run()方法
run() : 通常需要重写Thread类中的此方法, 将创建的线程要执行的操作声明在此方法中
currentThread() : 静态方法, 返回当前代码执行的线程
getName() : 获取当前线程的名字
setName() : 设置当前线程的名字
yield() : 释放当前CPU的执行权
join() : 在线程a中调用线程b的join(), 此时线程a进入阻塞状态, 知道线程b完全执行完以后, 线
程a才结束阻塞状态stop() : 已过时. 当执行此方法时,强制结束当前线程.
sleep(long militime) : 让线程睡眠指定的毫秒数,在指定时间内,线程是阻塞状态
isAlive() :判断当前线程是否存活
三、线程安全
多个线程操作一个对象,不会出现结果错乱的情况(缺失结果)
1、线程安全、线程不安全的例子
(1)安全 StringBuffer
public class SyncThreadA {
public static void main(String[] args) {
StringBuffer strB=new StringBuffer();
RunA r=new RunA(strB);
Thread a=new Thread(r);
a.start();
Thread b=new Thread(r);
b.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(strB.length());
}
}
//实现Runnable接口
class RunA implements Runnable{
StringBuffer strB;
public RunA(StringBuffer strB){
this.strB=strB;
}
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
strB.append("0");
}
}
}
(2)不安全 StringBuilder
public class SyncThreadA {
//StringBuilder就是线程不安全
public static void main(String[] args) {
StringBuilder strB=new StringBuilder();
//线程可以执行的任务
RunA r=new RunA(strB);
Thread a=new Thread(r);
a.start();
Thread b=new Thread(r);
b.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(strB.length());
}
}
//实现Runnable接口
class RunA implements Runnable{
StringBuilder strB;
public RunA(StringBuilder strB){
this.strB=strB;
}
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
strB.append("0");
}
}
}
2、同步
要做到线程安全,我们可以使用synchronized
对方法或者代码块加锁达到线程同步的效果
使用synchronized关键字修饰的方法或代码块,同一时间内,只能允许一个线程执行此代码
(1)synchronized关键字修饰方法
public class SyncThreadB {
//synchronized关键字修饰方法
public static synchronized void test(){
try {
System.out.println("------进入方法"+Thread.currentThread().getName());
Thread.sleep(1000);
System.out.println("------执行完毕"+Thread.currentThread().getName());
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
public static void main(String[] args) {
Runnable r=new RunB();
Thread a=new Thread(r);
Thread b=new Thread(r);
a.start();
b.start();
}
}
class RunB implements Runnable{
@Override
public void run() {
SyncThreadB.test();
}
}
(2)synchronized关键字修饰代码块
package com.easy7_24.thread;
public class SyncThreadB {
//synchronized关键字修饰代码块 即同步代码块
public static void testA(){
System.out.println("进入方法"+Thread.currentThread().getName());
synchronized (SyncThreadB.class){
System.out.println("进入同步代码块"+Thread.currentThread().getName());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("结束同步代码块"+Thread.currentThread().getName());
}
}
public static void main(String[] args) {
Runnable r=new RunB();
Thread a=new Thread(r);
Thread b=new Thread(r);
a.start();
b.start();
}
}
class RunB implements Runnable{
@Override
public void run() {
SyncThreadB.testA();
}
}
(3)实现原理
synchronized在JVM内部是通过Monitor(监视器)来实现的,Monitor是一种同步机制,它依赖于底层操作系统的Mutex Lock(互斥锁)来实现。Monitor与Java对象相关联,当线程尝试进入synchronized修饰的方法或代码块时,它会尝试获取与该对象关联的Monitor的锁。
Monitor的获取与释放:当线程进入synchronized块时,它会执行一个monitorenter指令,尝试获取对象关联的Monitor的锁。如果Monitor处于无锁状态,该线程会成功获取锁,并在对象头中标记为已锁定。如果Monitor已被其他线程锁定,则进入该方法的线程将被阻塞,直到获取到Monitor的锁。当线程退出synchronized块时,它会执行monitorexit指令,释放Monitor的锁,并允许其他等待的线程获取锁。在JVM中,每个Java对象在内存中都有特定的结构,通常分为三块区域:对象头(Object Header)、实例数据(Instance Data)和对齐填充(Padding)。其中,对象头是实现synchronized同步机制的关键部分。
对象头:对象头包含了对象的元数据信息,如哈希码(Hash Code)、GC分代年龄、锁状态标志、偏向线程ID、偏向时间戳等。这部分信息在JVM中用于支持对象的同步机制。
3、锁对象
锁对象 使用synchronized需要指定锁对象 synchronized修饰方法 成员方法 this 静态方法 类的类对象(描述了该类中定义了什么) 获取类对象使用obj.getClass() 或 Easy.class方法
(1)是同一个类对象
public class SyncThreadC {
static synchronized void syncMethod() {
System.out.println("进入同步方法" + Thread.currentThread().getName());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("结束同步方法" + Thread.currentThread().getName());
}
static void syncBlock() {
synchronized (SyncThreadC.class) {
System.out.println("进入同步代码块" + Thread.currentThread().getName());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("结束同步代码块" + Thread.currentThread().getName());
}
}
public static void main(String[] args) {
SyncThreadC obj=new SyncThreadC();
Runnable rc=new RunC(obj);
Runnable rd=new RunD(obj);
Thread a=new Thread(rc);
Thread b=new Thread(rd);
a.start();
b.start();
}
}
class RunC implements Runnable {
SyncThreadC c;
RunC( SyncThreadC c){
this.c=c;
}
@Override
public void run() {
System.out.println(c+"----method");
c.syncMethod();
}
}
class RunD implements Runnable {
SyncThreadC c;
RunD( SyncThreadC c){
this.c=c;
}
@Override
public void run() {
System.out.println(c+"----block");
c.syncBlock();
}
}
(2)不是同一个类对象
package com.easy7_24.thread;
public class SyncThreadC {
synchronized void syncMethod() {
System.out.println("进入同步方法" + Thread.currentThread().getName());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("结束同步方法" + Thread.currentThread().getName());
}
void syncBlock() {
synchronized (this) {
System.out.println("进入同步代码块" + Thread.currentThread().getName());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("结束同步代码块" + Thread.currentThread().getName());
}
}
public static void main(String[] args) {
Runnable rc=new RunC();
Runnable rd=new RunD();
Thread a=new Thread(rc);
Thread b=new Thread(rd);
a.start();
b.start();
}
}
class RunC implements Runnable {
@Override
public void run() {
new SyncThreadC().syncMethod();
}
}
class RunD implements Runnable {
@Override
public void run() {
new SyncThreadC().syncBlock();
}
}
(3)总结
一个锁对象可以看管多个方法,同一时间只允许一个线程通过,其它线程等待,无论它们访问的是不是同一个方法;而多个锁对象看管多个方法,只要被同一个锁对象看管的方法中没有正在执行的线程,那么线程就可以直接进去,不用等待。
synchronized的缺陷:当某个线程进入同步方法获得对象锁,那么其他线程访问这里对象的同步方法时,必须等待或者阻塞,如果这个方法代码比较多,那等待线程的时间就会比较长,直到当前执行方法的线程执行完方法,释放锁,等待线程才可以获得锁去执行。
(4) 资源的共享
通过Lambda表达式定义一个Runnable类型的匿名内部类,用该类创建线程a、b共用EasyThreadA类的静态属性list
是线程不安全的 由于
list
是共享的,并且被两个线程同时修改,最终list.size()
的输出是不可预测的
public class EasyThreadA {
static List list=new ArrayList();
public static void method(){
for(int i=0; i<10; i++){
list.add("A"+Thread.currentThread().getName());
}
}
public static void main(String[] args) {
Runnable run=EasyThreadA::method;
Thread a=new Thread(run);
Thread b=new Thread(run);
a.start();
b.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(list.size());
}
}
声明2个ThreadA类对象a、b,每个对象都有一个list属性
是线程不安全的,每个线程都有自己的
list
实例,容易资源浪费
public class EasyThreadA {
public static void method(){
for(int i=0; i<10; i++){
list.add("A"+Thread.currentThread().getName());
}
}
public static void main(String[] args) {
ThreadA a=new ThreadA();
ThreadA b=new ThreadA();
a.start();
b.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(a.list);
System.out.println(b.list);
}
}
class ThreadA extends Thread{
public List list=new ArrayList<>();
public void run(){
for (int i = 0; i < 10; i++) {
list.add("a");
}
}
}
通过定义1个Runnable接口实现类RunA run,创建线程a、b共用RunA run的list
public class EasyThreadA {
public static void method(){
for(int i=0; i<10; i++){
list.add("A"+Thread.currentThread().getName());
}
}
public static void main(String[] args) {
RunA run=new RunA();
Thread a=new Thread(run);
Thread b=new Thread(run);
a.start();
b.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(run.list);
System.out.println(run.list.size());
}
}
class RunA implements Runnable{
public List list=new ArrayList<>();
public void run(){
for (int i = 0; i < 10; i++) {
list.add("a");
}
}
}
4、 锁的分类
1 根据有无锁对象 悲观锁和乐观锁 悲观锁有锁对象 乐观锁没有锁对象 synchronized 是悲观锁 乐观锁的实现方式:CAS和版本号控制 Java里的锁都是悲观锁 2 公平锁和非公平锁 公平锁就是先来后到 非公平锁 Java里多为非公平锁 3 可重入锁 在同步代码块中遇到相同的锁对象的同步代码块,不需要在获取锁对象的权限,直接进 入执行 Java里都是可重入锁 4 根据线程状态不同 偏向锁(线程能直接进入执行), 轻量级锁(自旋锁 线程需自旋几圈才能进入执行 等待较短), 重量级锁(线程自旋圈数超过特定数目,进入阻塞状态,等待同步锁 此时CPU 不再分配资源 等待时间长)
5、乐观锁 CAS 版本号控制
乐观锁是基于版本号或时间戳的机制,在操作前做一个乐观的估计,如果操作成功,则版本号加1,如果失败,则重试。因为乐观锁不需要在整个操作期间占用资源的独占性,所以可以提高并发性。
Java中的乐观锁主要有两种实现方式:CAS和版本号控制。
(1)CAS
CAS是实现乐观锁的核心算法,它通过比较内存中的值是否和预期的值相等来判断是否存在冲突。如果存在,则返回失败;如果不存在,则执行更新操作。
CAS 它包含了 3 个参数:V(需要更新的变量)、E(预期值)和 N(最新值)。只有当需要更新的变量等于预期值时,需要更新的变量才会被设置为最新值,如果更新值和预期值不同,则说明已经有其它线程更新了需要更新的变量,此时当前线程不做操作,返回 V 的真实值。
Java中提供了AtomicInteger、AtomicLong、AtomicReference等原子类来支持CAS操作。
下面是一个简单的CAS示例:
public class Counter {
private AtomicInteger value = new AtomicInteger(0);
public void increment() {
int expect;
int update;
do {
expect = value.get();
update = expect + 1;
} while (!value.compareAndSet(expect, update));
}
}
在这个示例中,increment()方法会不断地执行CAS操作,直到更新成功为止。
(2)版本号控制
版本号控制是乐观锁的另一种实现方式。
每当一个线程要修改数据时,都会先读取当前的版本号或时间戳,并将其保存下来。线程完成修改后,会再次读取当前的版本号或时间戳,如果发现已经变化,则说明有其他线程对数据进行了修改,此时需要回滚并重试。
下面是一个简单的版本号控制示例:
public class Counter {
private int value = 0;
private int version = 0;
public void increment() {
int currentVersion;
int currentValue;
do {
currentVersion = version;
currentValue = value;
currentValue++;
} while (currentVersion != version);
value = currentValue;
version = currentVersion + 1;
}
}
在这个示例中,increment()方法会不断地执行循环,直到当前的版本号与保存的版本号相同为止。如果版本号不同,则说明有其他线程修改了数据,此时需要回滚并重试。
四、补充 BIO,NIO,AIO
I/O(输入/输出)操作是任何应用程序中必不可少的一部分,它涉及到与文件、网络或其他设备之间的数据传输。Java提供了几种不同的I/O模型,其中最常见的是AIO(异步非阻塞I/O)、BIO(阻塞I/O)和NIO(非阻塞I/O)。这些模型在处理I/O操作时具有不同的工作方式、特性和适用场景。
1、BIO(同步阻塞I/O)
BIO是最传统的I/O模型,也是Java最早引入的模型。在BIO中,当应用程序发起一个I/O操作时,线程会一直阻塞,直到操作完成。这意味着每个I/O请求都需要一个独立的线程,对于并发连接数较小的场景是可行的。然而,在大并发的情况下,BIO的性能会受到限制,因为每个连接都需要一个线程来处理,可能导致线程资源耗尽和性能下降。
2、NIO(同步非阻塞I/O)
Java 1.4引入了NIO,它提供了非阻塞的I/O操作,使得应用程序能够在等待I/O完成时进行其他任务,而无需阻塞线程。NIO使用选择器(Selector)来管理多个通道(Channel),一个线程可以处理多个通道的I/O操作。这种模型适用于需要处理多个连接但每个连接数据量较小的场景,如聊天室或通信服务器。尽管NIO相对于BIO来说更高效,但编程模型更复杂,需要更多的关注细节。
3、AIO(异步非阻塞I/O)
异步非阻塞I/O(AIO)是Java 7中引入的。AIO模型提供了异步读写操作,使得应用程序能够在等待I/O完成时继续执行其他任务,而不会阻塞等待。AIO适用于需要处理大量并发连接的场景,如高性能服务器开发。它通过回调机制在操作完成后通知应用程序,并可在此期间执行其他任务。虽然AIO在处理大量并发连接时效率较高,但它在关注点和编程模型方面与传统的阻塞I/O有很大区别,需要更高的技术要求。