Java多线程编程—wait/notify机制

news/2024/5/21 1:15:57/文章来源:https://blog.csdn.net/qq_43456605/article/details/130011881

文章目录

    • 1. 不使用wait/notify机制通信的缺点
    • 2. 什么是wait/notify机制
    • 3. wait/notify机制原理
    • 4. wait/notify方法的基本用法
    • 5. 线程状态的切换
    • 6. interrupt()遇到方法wait()
    • 7. notify/notifyAll方法
    • 8. wait(long)介绍
    • 9. 生产者/消费者模式
    • 10. 管道机制
    • 11. 利用wait/notify实现交叉备份
    • 12. 方法Sleep()和wait()的区别

线程是操作系统中独立的个体,但这些个体如果不经过特殊的处理时不能成为一体的。线程间的通信就是使线程称为整体的必用方案之一,可以说,使线程之间进行通信后系统之间的交互性会更大,CPU利用效率得以大幅提升,同时程序员在处理过程中可以有效把控于监督各线程任务。

1. 不使用wait/notify机制通信的缺点

下面模拟不使用wait/notify机制进行通信,然后分析其缺点所在

public class multithreadingtest {static volatile private List list=new ArrayList() ;   //TODO 使用volatile修饰,可以实现线程之间数据的可见性public static void main(String[] args) {Thread thread1=new Thread(new Runnable() {   //TODO 线程thread1实现循环向list中添加元素@Overridepublic void run() {int i=1;while(i>0){list.add(i);  //TODO 向集合中添加元素System.out.println("添加了"+i+"个元素");i++;try {Thread.sleep(1000);  //TODO 这里睡眠是为了等待Thread2执行} catch (InterruptedException e) {e.printStackTrace();}}}});Thread thread2=new Thread(new Runnable() {   //TODO 线程thread2判读集合中元素中的数量是否达到了5@Overridepublic void run() {try {while(true){if(list.size()==5){System.out.println("thread2要退出了");throw new InterruptedException();  //TODO 以抛出异常的方式结束线程}}}catch (InterruptedException e){e.printStackTrace();}}});thread1.setName("thread1");thread2.setName("thread2");  //TODO 设置进程名thread1.start();thread2.start();}}

在这里插入图片描述

分析代码,这里的线程间通信是指,thread1向集合中添加的数据达到5个后,通知线程2结束。虽然两个线程以集合为媒介完成了通信,但还存在缺点,thread2需要使用while循环不断轮询检测某一个条件,这样会很浪费CPU资源,所以需要引入一种机制,在减少CPU资源浪费的同时,还可以实现在多个线程之间的通信,这就是wait/notify机制。

2. 什么是wait/notify机制

wait/notify机制是Java中用于线程间通信的一种机制。它基于“监视器对象”(monitor object)的概念,通过在多个线程之间共享一个对象锁来实现线程的协作。在wait/notify机制中,线程可以调用对象的wait()方法来阻塞自己,直到其他线程调用同一个对象的notify()或notifyAll()方法来唤醒它。当一个线程调用wait()方法时,它会释放对象的锁,使其他线程能够获取该对象的锁并执行相应的操作。当其他线程调用notify()或notifyAll()方法时,它们会唤醒等待在该对象上的线程,使它们重新竞争对象的锁,继续执行操作。使用wait/notify机制可以实现一些复杂的线程协作模式,比如生产者消费者模式、读写锁模式等。但是在使用过程中需要特别注意避免死锁、竞态条件等问题。

3. wait/notify机制原理

注意:拥有相同锁的线程才可以实现wait/notify机制。
wait()方法

wait()Object类的方法,它的作用是使当前执行该方法的进程进行等待,在wait()所在的代码处暂停执行,并释放锁

  • 在调用wait之前,该线程必须要获得该对象的对象级别锁
  • 如果wait之前没有持有适当的锁,抛出IllegalMonitorStateException
  • 通过通知机制使得某个线程继续执行wait方法后面的代码,对线程的选择是按wait方法的顺序确定的(可能多个线程wait的情况)

notify()方法

该方法用于唤醒当前正在wait的线程,对线程的选择是按wait方法的顺序确定的

  • 调用notigy方法必须是在同步方法或同步块中调用,即调用之前也要获得锁,否则抛出IllegalMonitorStateException
  • 执行notify方法后,当前线程不会立即释放该锁,而是等到执行notify方法的线程将同步区代码全部执行完毕后才释放锁

4. wait/notify方法的基本用法

wait()方法使当前线程暂停运行,并释放锁

  1. 模拟一:模拟未获取锁执行wait方法
    public static void main(String[] args) throws InterruptedException {String name=new String();name.wait();}

在这里插入图片描述
2. 模拟二:wait()方法的正确使用

  public static void main(String[] args) throws InterruptedException {String name=new String();synchronized (name){System.out.println("wait前");name.wait();System.out.println("wait后");}}

在这里插入图片描述
3. 模拟三:wait/notify机制的使用

  public static void main(String[] args) throws InterruptedException {Thread thread1=new Thread(new Runnable() {@Overridepublic void run() {synchronized (list) {System.out.println("thread1跑起来了");try {list.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("thread1跑完了");}}});Thread thread2=new Thread(new Runnable() {@Overridepublic void run() {synchronized (list){System.out.println("thread2跑起来了");list.notify();System.out.println("thread2跑完了");}}});thread1.start();Thread.sleep(3000);thread2.start();}

在这里插入图片描述

除了notify()方法外还有一个notifyAll()方法,该方法执行后会按照执行wait()方法的倒序依次唤醒“全部”的线程

5. 线程状态的切换

Thread中存在着很多的方法可以改变线程对象的状态
在这里插入图片描述

  • 创建一个新的线程对象后,再调用它的start()方法,系统会为该线程分配CPU资源,处于可运行状态(就绪状态),这个一个准备运行的状态,如果线程抢占倒cpu资源就会转移到运行状态
  • 可运行状态和运行状态是可以相互切换的。运行状态的线程可能会被高优先级的线程抢占资源从而进入就绪态
  • 线程进入可运行状态的情况
    • 调用sleep方法超过指定休眠时间
    • 线程获得了同步监视器(锁)
    • 线程在等待某个通知,而相应的线程发送了通知
    • 处于挂起(等待)状态的线程调用了resume方法
  • 暂停(等待)状态结束后,线程进入可运行状态等待系统分配资源,出现阻塞的情况大体分为5种
    • 线程调用Sleep方法,主动放弃占用的处理器资源
    • 线程调用了阻塞式I/O方法(即线程忙着处理I/O操作了),在该方法返回前,线程被阻塞
    • 线程尝试获取某个对象的锁,但锁被占用
    • 线程等待某个通知
    • 程序调用了suspend方法将该线程挂起,此方法容易导致死锁,尽量避免
  • run方法执行完毕后进入线程销毁阶段,整个线程执行完毕

Sleep()方法和wait()方法一样也会让线程陷入阻塞,但sleep并不会让持有锁的线程释放锁,而锁陷入等待(这是一种同步效果)

6. interrupt()遇到方法wait()

当线程调用wait()方法后,再对该线程对象执行interrupt()方法时,会出现InterruptedException异常

public static void main(String[] args) throws InterruptedException {Thread thread1=new Thread(new Runnable() {@Overridepublic void run() {synchronized (list) {System.out.println("thread1跑起来了");try {list.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("thread1跑完了");}}});thread1.start();thread1.interrupt();}

在这里插入图片描述

7. notify/notifyAll方法

每次调用notify方法,只通知一个线程进行唤醒,唤醒的顺序按执行wait()方法的正序。

public static void main(String[] args) throws InterruptedException {Thread thread1=new Thread(new Runnable() {@Overridepublic void run() {synchronized (list) {System.out.println("第一个wait");try {list.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}list.notify();System.out.println("第一个结束");}}});Thread thread2=new Thread(new Runnable() {@Overridepublic void run() {synchronized (list) {System.out.println("第二个wait");try {list.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}list.notify();System.out.println("第二个结束");}}});Thread thread3=new Thread(new Runnable() {@Overridepublic void run() {synchronized (list) {System.out.println("第三个wait");try {list.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}list.notify();System.out.println("第三个结束");}}});Thread thread4=new Thread(new Runnable() {@Overridepublic void run() {synchronized (list){System.out.println("开始通知");list.notify();System.out.println("通知结束");}}});thread1.start();thread2.start();thread3.start();Thread.sleep(3000);thread4.start();}

在这里插入图片描述

总结

  • 执行notify方法后,按照wait执行顺序唤醒其它进行
  • 执行同步代码块(临界区)过程中,遇到异常而导致线程终止时,锁也会被释放
  • 执行临界区代码中执行了锁所属对象的wait方法,线程会释放对象锁,等待被唤醒

nofity一次调用只能唤醒一个线程,所以容易出现部分线程对象没有被唤醒的情况。为了唤醒全部线程,可以使用notifyAll方法,它会按照执行wait方法的倒序一次对线程进行唤醒的。

8. wait(long)介绍

带有一个参数的wait(long)方法的功能是等待某一个时间内是否有线程进行通知唤醒,如果超过这个时间则自动唤醒。能继续向下运行的前提是再次持有锁。(注意也是可以在long时间内被其它线程唤醒的)

    public static void main(String[] args) throws InterruptedException {Thread thread1=new Thread(new Runnable() {@Overridepublic void run() {synchronized (list) {System.out.println("开始时间:"+System.currentTimeMillis());try {list.wait(2000);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("开始时间:"+System.currentTimeMillis());}}});thread1.start();}

在这里插入图片描述

9. 生产者/消费者模式

生产者/消费者模式:在一个系统中,存在生产者和消费者两种角色,他们通过内存缓冲区进行通信,生产者生产消费者需要的资料,消费者把资料做成产品。wait/notify机制最经典的案例“生产者与消费者模式”模式。
在这里插入图片描述
案例一:单生产者和单消费者模式
一个生产者生产,一个消费者消费,没有产品时生产者才能生产,有产品时消费者才能消费

//消费者
public class P {private String lock;public P(String lock){super();this.lock=lock;}public void GetValue(){try{synchronized (lock){if(ValueObject.value.equals("")){   //value不为空说明消费者还没有消费,所以生产者需要等待lock.wait();}System.out.println("我现在要开始消费了!");lock.notify();System.out.println("消费的产品:"+ValueObject.value);ValueObject.value="";}} catch (InterruptedException e) {throw new RuntimeException(e);}}
}
//生产者
public class C {private String lock;public C(String lock){super();this.lock=lock;}public void SetValue(){try{synchronized (lock){if(!ValueObject.value.equals("")){   //value不为空说明消费者还没有消费,所以生产者需要等待lock.wait();}System.out.println("我现在要开始生产了!");lock.notify();String value= String.valueOf(System.currentTimeMillis())+"_"+String.valueOf(System.nanoTime());System.out.println("生产的产品:"+value);ValueObject.value=value;}} catch (InterruptedException e) {throw new RuntimeException(e);}}
}
//消费者线程
public class ThreadB extends Thread{private P p;public ThreadB(P p){super();this.p=p;}@Overridepublic void run(){while(true){  //消费者消费p.GetValue();}}
}
//生产者线程
public class ThreadA extends Thread{private C c;public ThreadA(C c){super();this.c=c;}@Overridepublic void run(){while(true){  //生产者不断生产c.GetValue();}}
}
//main函数public static void main(String[] args) {String lock=new String("");P p=new P(lock);C c=new C(lock);ThreadA threadA=new ThreadA(p);ThreadB threadB=new ThreadB(c);threadA.start();threadB.start();}

在这里插入图片描述
案例二:多生产者和多消费者模式的问题

在上面案例代码的基础上,多增加生产者,使得生产者的数据远远大于消费者,会出现连续生产的问题,导致生产的内容会发生覆盖,而消费者的数量远远大于生产者的数量时,会发生消费者消费空值的情况。原因是if条件发生改变时,其它的线程并不知道。同时还会出现唤醒同类的情况,最终出现连续生产或连续消费,导致程序的逻辑出现错误。解决这个问题的方法就是把if判断改为while判断。

写覆盖代码

public class multithreadingtest {public static void main(String[] args) {String lock=new String("");P p=new P(lock);C c=new C(lock);ThreadA[] pThread=new ThreadA[20];ThreadB[] rThread=new ThreadB[20];for (int i = 0; i < 20; i++) {pThread[i]=new ThreadA(c);pThread[i].setName("生产者"+(i+1));pThread[i].start();}for (int i = 0; i < 2; i++) {rThread[i]=new ThreadB(p);rThread[i].setName("消费者"+(i+1));rThread[i].start();}}
}

在这里插入图片描述

消费空值代码

public class multithreadingtest {public static void main(String[] args) {String lock=new String("");P p=new P(lock);C c=new C(lock);ThreadA[] pThread=new ThreadA[20];ThreadB[] rThread=new ThreadB[20];for (int i = 0; i < 2; i++) {pThread[i]=new ThreadA(c);pThread[i].setName("生产者"+(i+1));pThread[i].start();}for (int i = 0; i < 20; i++) {rThread[i]=new ThreadB(p);rThread[i].setName("消费者"+(i+1));rThread[i].start();}}
}

在这里插入图片描述

将多生产多消费情况下的if判断条件改为while,就不会出现连续生产和连续消费问题,即使唤醒同类线程还是会执行while判断条件,如果条件为true则执行wait方法,避免了连续生产和连续消费的情况,但程序运行过程中却出现了假死,也就是所有的线程都呈现等待状态。“假死”的现象就是线程进入等待状态,如果全部线程都进入了等待状态,程序就不执行任何业务功能了,整个项目呈现等待状态。
在这里插入图片描述
上面的情况用一种简单情形解释:老爸老妈都会做饭,你和你哥都要吃饭。老妈做完饭通知你和你哥吃饭然后去休息(释放锁开始等待),老爸一看饭做好了,不用他去做了就去休息了(生产者唤醒生产者线程),你哥还是下班的路上,你先去吃饭,结果吃完了,然后还没心没肺的通知哥回来吃饭(消费者线程唤醒消费者线程),你哥到家后一看没有饭,也去休息了,这样所有的线程都休息了,所以出现了假死。假死出现的极大可能原因是连续唤醒了同类。解决假死的方法就是将notify通知换为notifyAll通知,这样就能顺利解决假死问题。

10. 管道机制

管道机制是线程之间消息传递的一种机制,Java语言提供了各种各样的输入输出流,使我们能够很方便地操作数据,其中管道流是一种特殊的流,用于在不同线程间之间传送数据。一个线程发送数据输出到管道,另一个线程从输入管道中读数据,通过使用管道,实现不同线程间的通信,而无须借助与临时文件之类的东西。
测试一:在管道中传递字节流

//输入管道
public class wirteMethod {public void writMethod(PipedOutputStream out)  //参数为管道{try {System.out.println("wirte:");for (int i = 0; i < 300; i++) {String outData = "" + (i + 1);out.write(outData.getBytes()); //向管道中写System.out.print("~~~"+outData);}System.out.println();out.close();} catch (IOException e) {throw new RuntimeException(e);}}
}
//输出管道
public class ReadDate {public void readMethod(PipedInputStream input){try{System.out.println("read:");byte[] byteArray=new byte[20];int readLength=input.read(byteArray);while (readLength!=-1){String newData=new String(byteArray,0,readLength);System.out.print("____"+newData);readLength=input.read(byteArray);}System.out.println();input.close();} catch (IOException e) {throw new RuntimeException(e);}}
}
//输入线程
public class ThreadA extends Thread{private wirteMethod wirteMethod;private PipedOutputStream pipedOutputStream;public ThreadA(wirteMethod wirteMethod,PipedOutputStream pipedOutputStream){super();this.wirteMethod=wirteMethod;this.pipedOutputStream=pipedOutputStream;}@Overridepublic void run(){wirteMethod.writMethod(pipedOutputStream);}
}
//输出线程
public class ThreadB extends Thread{private ReadDate readDate;private PipedInputStream pipedInputStream;public ThreadB(ReadDate readDate,PipedInputStream pipedInputStream){super();this.readDate=readDate;this.pipedInputStream=pipedInputStream;}@Overridepublic void run(){readDate.readMethod(pipedInputStream);}
}
//测试
public class multithreadingtest {public static void main(String[] args) throws IOException, InterruptedException {wirteMethod  wirteMethod=new wirteMethod();ReadDate readDate=new ReadDate();PipedInputStream inputStream=new PipedInputStream();PipedOutputStream outputStream=new PipedOutputStream();inputStream.connect(outputStream);outputStream.connect(inputStream); //使两个管道之间产生通信链接ThreadB readThread=new ThreadB(readDate,inputStream);  //读线程先准备readThread.setName("read");readThread.start();Thread.sleep(2000);ThreadA wirteThread=new ThreadA(wirteMethod,outputStream);wirteThread.setName("witte");wirteThread.start();}
}

在这里插入图片描述
从输出结果可以看出,首先是读线程启动,由于当前管道中没有数据被写入,所以线程阻塞,知道有数据写入才继续向下运行
测试二:在管道中传递字符流(基本同上,这里就不演示了)

11. 利用wait/notify实现交叉备份

利用wati/notify机制实现交叉备份:创建20个线程,其中10个线程是向数据库A中备份数据,另外10个线程向数据库B中备份数据,要满足备份工作是交叉进行的(往A备份,然后下次往B备份,轮流交叉),重点是如何利用wait/notify让20个线程变得有序

//备份数据的代码。
public class DBTools {volatile private boolean prevIsa=false; //信号量synchronized public void backupA(){ //A备份try {while(prevIsa==true){  //prevIsa为true,此时则等待wait();}for (int i = 0; i < 5; i++) { //备份五次System.out.println("&&&&&&&&&");}prevIsa=true;notifyAll();} catch (InterruptedException e) {throw new RuntimeException(e);}}synchronized public void backupB(){try { while(prevIsa==false){  wait();}for (int i = 0; i < 5; i++) {System.out.println("*********");}prevIsa=false;notifyAll();} catch (InterruptedException e) {throw new RuntimeException(e);}}
}
//a备份线程
public class ThreadA extends Thread{private  DBTools dbTools;public ThreadA(DBTools dbTools){super();this.dbTools=dbTools;}@Overridepublic void run(){dbTools.backupA();}
}
//b备份线程
public class ThreadB extends Thread{private  DBTools dbTools;public ThreadB(DBTools dbTools){super();this.dbTools=dbTools;}@Overridepublic void run(){dbTools.backupB();}
}
//测试代码
public class multithreadingtest {public static void main(String[] args) throws IOException, InterruptedException {DBTools dbTools=new DBTools();for (int i = 0; i < 20; i++) {ThreadA threadA=new ThreadA(dbTools);threadA.start();ThreadB threadB=new ThreadB(dbTools);threadB.start();}}
}

在这里插入图片描述
交替打印的效果说明AB是交替进行备份的

12. 方法Sleep()和wait()的区别

方法sleep()和Wait()的区别
(1) sleep()是Thread类中的方法,二wait()是Object中的方法
(2) sleep()可以不结合Sysnchronized使用 ,而wait()必须结合
(3) sleep()在执行时不会释放锁,而wait()在执行后锁被释放
(4) sleep()方法执行后线程的状态时TIMED_WAITING,wait()方法执行后线程的状态是等待

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_283419.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Bert的MLM任务loss原理

bert预训练有MLM和NSP两个任务&#xff0c;其中MLM是类似于“完形填空”的方式&#xff0c;对一个句子里的15%的词进行mask&#xff0c;通过双向transformerfeedforwardrediual_addlayer_norm完成对每个词的embedding编码&#xff0c;然后对mask的这个词进行预测&#xff0c;预…

【记录Bug】IDEA提示“Error:java: 错误: 不支持发行版本 17”

项目场景&#xff1a; 开发工具&#xff1a;IDea 后端框架&#xff1a;SpringBoot 问题描述 在rebuild或运行项目时提示“Error:java: 错误: 不支持发行版本 17”。 这个错误表明你的IDEA版本不支持使用Java 17。你需要将项目编译运行环境设置为更低版本的Java&#xff0c;或…

总结MySQL、Redis的优化措施与使用 mysql_upgrade升级数据结构

目录 一.MySQL数据库优化 二.Redis优化 三.MySQL创建测试账号报错 一.MySQL数据库优化 遵循MySQL层优化的五个原则: 减少数据访问&#xff0c;返回更少的数据&#xff0c;减少交互次数减少服务器CPU开销&#xff0c;利用更多资源。理解SQL优化原理并进行SQL优化&#xff0c…

力扣:字符串中的第一个唯一字符(C++实现)

题目部分&#xff1a; 解题思路&#xff1a; 方案一&#xff1a; 首先认真审题的小伙伴们一定会发现就是题目给了提示只包含小写字母&#xff0c;也就是说我们的排查范围是小写的26个字母。为了怕有的友友们一时短路想不起来&#xff0c;我就其按照顺序列出来吧。 即&#x…

[架构之路-157]-《软考-系统分析师》- 9-信息系统规划-2-少量人力进行项目初步调研(系统分析师的首要任务)与可行性研究报告

目录 9 . 3 初步调查 1. 初步调查的目标 9.4可行性研究 9.4.1可行性评价准则 1 . 经济可行性&#xff08;钱的可行性&#xff09; 2 . 技术可行性&#xff08;能力可行性&#xff09; 3 . 法律可行性&#xff08;社会&#xff09; 4 . 用户使用可行性&#xff08;用户&…

洛谷P2822:组合数问题 ←(帕斯卡法则+取模+前缀和)

【题目来源】https://www.luogu.com.cn/problem/P2822【题目描述】 组合数​表示的是从n个物品中选出m个物品的方案数。举个例子&#xff1a;从(1,2,3)三个物品中选择两个物品可以有(1,2)&#xff0c;(1,3)&#xff0c;(2,3) 这三种选择方法。根据组合数的定义&#xff0c;我们…

属性配置的宏(修改宏IntDir)

项目属性页码 拷贝下 常见宏列表 下表描述了可用宏的常用子集&#xff1b;还有很多没有在这里列出。 转到“宏”对话框&#xff0c;查看项目中的所有属性及其当前值。 有关如何创建 MSBuild 属性定义以及如何在 .props、.targets 和 .vcxproj 文件中将其用作宏的详细信息&am…

面向对象编程(进阶)7:面向对象特征三:多态性

一千个读者眼中有一千个哈姆雷特。 目录 7.1 多态的形式和体现 7.1.1 对象的多态性 举例&#xff1a; 7.1.2 多态的理解 7.1.3 举例 1、方法内局部变量的赋值体现多态 2、方法的形参声明体现多态 3、方法返回值类型体现多态 7.2 为什么需要多态性(polymorphism)&#x…

登录认证功能的实现

1.登录校验分析 什么是登录校验&#xff1f; 所谓登录校验&#xff0c;指的是我们在服务器端接收到浏览器发送过来的请求之后&#xff0c;首先我们要对请求进行校验。先要校验一下用户登录了没有&#xff0c;如果用户已经登录了&#xff0c;就直接执行对应的业务操作就可以了…

C语言头文件路径相关问题总结说明

聊聊系统路径位置&#xff0c;绝对路径与相对路径&#xff0c;正斜杠 / 与 反斜杠 \ 使用说明 ...... by 矜辰所致目录前言一、C语言中的头文件引用二、KEIL 中的头文件路径2.1 IncudePaths 指定的路径绝对路径和相对路径正斜杠 / 与 反斜杠 \ 与双斜杠2.2 include < >…

VN5620以太网测试——环境搭建篇

文章目录 前言一、新建以太网工程二、Port Configuration三、Link up四 Trace界面五、添加Ethernet Packet Builder六、添加ARP Packet七、添加Ethernet IG总结前言 CANoe(CAN open environment)VN5620 :是一个紧凑而强大的接口,用于以太网网络的分析、仿真、测试和验证。 …

页面预加载优化实践

概述在客户端开发中&#xff0c;列表类型页面大多都依赖网络请求&#xff0c;需要等网络数据请求下来后再刷新页面。但遇到网络请求慢的场景&#xff0c;就会导致页面加载很慢甚至加载失败。我负责会员的商品列表页面&#xff0c;在业务场景中&#xff0c;页面元素比较复杂&…

初学对象存储OSS---学习笔记

文章目录前言一、OSS是什么&#xff1f;下面以一个小故事介绍OSS的作用&#xff1a;二、怎么使用OSS1.进入 -----> [阿里云官网](https://www.aliyun.com/)2.点击进入OSS控制台3.获取accessKeyId 和 accessKeySecret4.拿到了accessKeyId 和 accessKeySecret &#xff0c;就可…

[Netty] Netty与Os的零拷贝 (五)

1.Netty的零拷贝原理 Netty接收和发送ByteBuffer采用DirectBuffer&#xff0c;使用堆外直接内存进行Socket读写&#xff0c;不需要进行字节缓冲区的二次拷贝。如果使用传统的JVM的堆内存&#xff08;Heap Buffer&#xff09;进行socker读写&#xff0c;那么JVM将会将堆内存拷贝…

javaScript---理解异步

目录 同步与异步 进程和线程 JS单线程 定时器 同步与异步 同步&#xff1a;调用之后得到结果再干别的任务。 异步&#xff1a;调用之后先不管结果&#xff0c;继续干别的任务。 进程和线程 进程&#xff08;process&#xff09;程序运行的实例&#xff0c;同一个程序可以产生多…

RestClient操作文档

RestClient操作文档5.RestClient操作文档5.1.新增文档5.1.1.索引库实体类5.1.2.语法说明5.1.3.完整代码5.2.查询文档5.2.1.语法说明5.2.2.完整代码5.3.删除文档5.4.修改文档5.4.1.语法说明5.4.2.完整代码5.5.批量导入文档5.5.1.语法说明5.5.2.完整代码5.6.小结5.RestClient操作…

多测合一生产软件SISS教程大全

下载&#xff1a; 在这里插入代码片产品介绍&#xff1a; 不动产权籍调查测绘软件RESS是南方数码针对不动产权籍调查测绘工作研发的一款专业的数据处理软件&#xff0c;融合了业内主流测绘软件&#xff08;南方数码地形地籍成图软件CASS和新一代房测之友BMFse&#xff09;的核…

C++ 一些在工作中遇到的小问题的c++解决脚本程序,记录用的

文章目录前言1 从"txt"文件中读取数组2 线性插值生成数组后存入“.txt”文件前言 本博客用于记录在工作中遇到的一些C小脚本。 1 从"txt"文件中读取数组 txt内文本如下图所示&#xff0c;按行记录数据&#xff0c;每组数据包含第一列为Index即索引号&…

【学习提高】JVM垃圾收集器,垃圾回收算法,一个对象从创建到回收的过程。

1、JVM垃圾收集器 不同的垃圾回收器&#xff0c;适用于不同的场景。常用的垃圾回收器&#xff1a;   串行&#xff08;Serial&#xff09;回收器是单线程的一个回收器&#xff0c;简单、易实现、效率高。   并行&#xff08;ParNew&#xff09;回收器是Serial的多线程版&…

面向对象编程(基础)1:面向对象思想精髓的理解

目录 1. 面向对象编程概述(了解) 1.1 程序设计的思路 1. 面向过程的程序设计思想&#xff08;Process-Oriented Programming&#xff09;&#xff0c;简称POP 2. 面向对象的程序设计思想&#xff08; Object Oriented Programming&#xff09;&#xff0c;简称OOP 1.2 由实…