当前位置: 首页 > news >正文

并发编程---阻塞队列(五)

阻塞队列

  • 一 阻塞队列
    • 1.1.阻塞队列概念
    • 1.2.阻塞队列API案例
      • 1.2.1. ArrayBlockingQueue
        • 1.2.1.1.抛出异常
        • 1.2.1.2.返回布尔
        • 1.2.1.3.阻塞
        • 1.2.1.4.超时
      • 1.2.2.SynchronousQueue
  • 二 阻塞队列应用---生产者消费者
    • 2.1.传统模式
      • 案例代码
      • 结果
      • 案例问题---防止虚假唤醒
    • 2.2.⽣产者消费者防⽌虚假唤醒
      • 2.2.1 新版⽣产者消费者写法 ReentrantLock.Condition
        • 案例代码
      • 2.2.2精准通知顺序访问
        • 案例代码
    • 2.3.Synchronized和Lock的区别
    • 2.4. 阻塞队列模式⽣产者消费者
      • 案例代码

在这里插入图片描述

一 阻塞队列

1.1.阻塞队列概念

概念:

  • 在多线程领域:所谓阻塞,在某些情况下会挂起线程(即阻塞),⼀旦条件满⾜,被挂起的线程⼜ 会⾃动被唤醒。
  • 阻塞队列 是⼀个队列,在数据结构中起的作⽤如下图:
    在这里插入图片描述

当队列是空的,从队列中获取(Take)元素的操作将会被阻塞
当队列是满的,从队列中添加(Put)元素的操作将会被阻塞
试图中空的队列中获取元素的线程将会被阻塞,直到其他线程往空的队列插⼊新的元素
试图向已满的队列中添加新元素的线程将会被阻塞,直到其他线程从队列中移除⼀个或多个元素 或者完全清空,使队列变得空闲起来后并后续新增

好处:阻塞队列不⽤⼿动控制什么时候该被阻塞,什么时候该被唤醒,简化了操作。
体系: Collection→ Queue→ BlockingQueue→七个阻塞队列实现类。
在这里插入图片描述
在这里插入图片描述
粗体标记的三个⽤得⽐较多,许多消息中间件底层就是⽤它们实现的。
需要注意的是 LinkedBlockingQueue 虽然是有界的,但有个巨坑,其默认⼤⼩是 Integer.MAX_VALUE ,⾼达21亿,⼀般情况下内存早爆了(在线程池的 ThreadPoolExecutor 有体现)。
API:

抛出异常是指: 当队列满时,再次插⼊会抛出异常;
返回布尔是指:当队列满时,再次插⼊会返回false;
阻塞是指:当队列满时,再次插⼊会被阻塞,直到队列取出⼀个元素,才能插⼊。
超时是指:当⼀个时限过后,才会插⼊或者取出。

在这里插入图片描述

1.2.阻塞队列API案例

1.2.1. ArrayBlockingQueue

1.2.1.1.抛出异常

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
代码

public class BlockingQueueDemo {public static void main(String[] args) throws InterruptedException {//定义容量为3的阻塞队列BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<String>(3);System.out.println(blockingQueue.add("a"));System.out.println(blockingQueue.add("b"));System.out.println(blockingQueue.add("c"));System.out.println(blockingQueue.add("e"));System.out.println(blockingQueue.element());System.out.println(blockingQueue.remove());System.out.println(blockingQueue.remove());System.out.println(blockingQueue.remove());System.out.println(blockingQueue.remove());}

结果分析

  1. 当添加四个元素时抛出IllegalStateException: Queue full

在这里插入图片描述

  1. 当取出四个元素时抛出NoSuchElementException

在这里插入图片描述

1.2.1.2.返回布尔

在这里插入图片描述
代码

  public static void main(String[] args) throws InterruptedException {//定义容量为3的阻塞队列BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<String>(3);System.out.println(blockingQueue.offer("a"));System.out.println(blockingQueue.offer("b"));System.out.println(blockingQueue.offer("c"));System.out.println(blockingQueue.offer("e"));System.out.println(blockingQueue.peek());System.out.println(blockingQueue.poll());System.out.println(blockingQueue.poll());System.out.println(blockingQueue.poll());System.out.println(blockingQueue.poll());}

结果

在这里插入图片描述

1.2.1.3.阻塞

代码

 public static void main(String[] args) throws InterruptedException {//定义容量为3的阻塞队列BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<String>(3);blockingQueue.put("a");blockingQueue.put("b");blockingQueue.put("c");blockingQueue.put("d");System.out.println(blockingQueue.take());System.out.println(blockingQueue.take());System.out.println(blockingQueue.take());System.out.println(blockingQueue.take());}

结果

  1. 存储是容量已满
    在这里插入图片描述
  2. 取得时候队列为空也会阻塞
    在这里插入图片描述

1.2.1.4.超时

代码

  public static void main(String[] args) throws InterruptedException {//定义容量为3的阻塞队列BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<String>(3);System.out.println(blockingQueue.offer("a",2L, TimeUnit.SECONDS));System.out.println(blockingQueue.offer("a",2L, TimeUnit.SECONDS));System.out.println(blockingQueue.offer("a",2L, TimeUnit.SECONDS));System.out.println(blockingQueue.offer("a",2L, TimeUnit.SECONDS));}

结果
在这里插入图片描述

1.2.2.SynchronousQueue

队列只有⼀个元素,如果想插⼊多个,阻塞到队列元素取出后,才能插⼊,只能有⼀个“坑位”,⽤⼀个
插⼀个,详⻅SynchronousQueueDemo。
代码


public class SynchronousQueueDemo {public static void main(String[] args) {BlockingQueue<String> blockingQueue=new SynchronousQueue<String>();new Thread(()->{try {System.out.println(Thread.currentThread().getName()+"\t put 1");blockingQueue.put("1");System.out.println(Thread.currentThread().getName()+"\t put 2");blockingQueue.put("2");System.out.println(Thread.currentThread().getName()+"\t put 3");blockingQueue.put("3");} catch (InterruptedException e) {e.printStackTrace();}},"AAA").start();new Thread(()->{try {try{ TimeUnit.SECONDS.sleep(5); }catch (InterruptedException e){ e.printStackTrace(); }System.out.println(Thread.currentThread().getName()+"\t take "+blockingQueue.take());try{ TimeUnit.SECONDS.sleep(5); }catch (InterruptedException e){ e.printStackTrace(); }System.out.println(Thread.currentThread().getName()+"\t take "+blockingQueue.take());try{ TimeUnit.SECONDS.sleep(5); }catch (InterruptedException e){ e.printStackTrace(); }System.out.println(Thread.currentThread().getName()+"\t take"+blockingQueue.take());} catch (Exception e) {e.printStackTrace();}},"BBB").start();}
}

结果
在这里插入图片描述

二 阻塞队列应用—生产者消费者

2.1.传统模式

传统模式使⽤ Synchronized来进⾏操作。

案例代码

/*** 题目:现在两个线程,可以操作初始值为零的一个变量,* 实现一个线程对该变量加1,一个线程对该变量-1,* 实现交替,来10轮,变量初始值为0.* 1.高内聚低耦合前提下,线程操作资源类* 2.判断/干活/通知* 3.防止虚假唤醒(判断只能用while,不能用if)* 知识小总结:多线程编程套路+while判断+新版写法*/
public class ProdConsumerDemo {public static void main(String[] args) {Aircondition aircondition = new Aircondition();new Thread(() -> {for (int i = 1; i <= 10; i++) {try {aircondition.increment();} catch (Exception e) {e.printStackTrace();}}}, "A").start();new Thread(() -> {for (int i = 1; i <= 5; i++) {try {aircondition.decrement();} catch (Exception e) {e.printStackTrace();}}}, "B").start();}
}
class Aircondition {private int number = 0;//⽼版写法public synchronized void increment() throws Exception {//1.判断if (number != 0) {this.wait();}//2.⼲活number++;System.out.println(Thread.currentThread().getName() + "\t" + number);//3通知this.notifyAll();}public synchronized void decrement() throws Exception {//1.判断if (number == 0) {this.wait();}//2.⼲活number--;System.out.println(Thread.currentThread().getName() + "\t" + number);//3通知this.notifyAll();}
}

结果

在这里插入图片描述

案例问题—防止虚假唤醒

上述我们只用了一个线程作为生产者,一个线程作为消费者,我们用多个来进行测试。防止虚假唤醒(判断只能用while,不能用if)

while循环与if判断
while是循环语句,当满足条件时执行语句,执行完循环以后再回来判断是否满足,满足继续执行,然后继续判断,不满足直接执行下面的语句
if是判断语句,满足条件就行,执行完以后继续执行下面的语句,不会再回来判断执行。

A线程生产包子,B线程消费包子,C线程生产包子,D线程消费包子
在这里插入图片描述

public class ProdConsumerDemo {public static void main(String[] args) {Aircondition aircondition = new Aircondition();new Thread(() -> {for (int i = 1; i <= 10; i++) {try {aircondition.increment();} catch (Exception e) {e.printStackTrace();}}}, "A").start();new Thread(() -> {for (int i = 1; i <= 5; i++) {try {aircondition.decrement();} catch (Exception e) {e.printStackTrace();}}}, "B").start();new Thread(() -> {for (int i = 1; i <= 10; i++) {try {aircondition.increment();} catch (Exception e) {e.printStackTrace();}}}, "C").start();new Thread(() -> {for (int i = 1; i <= 10; i++) {try {aircondition.decrement();} catch (Exception e) {e.printStackTrace();}}}, "D").start();}
}

Aircondition

class Aircondition {private int number = 0;//⽼版写法public synchronized void increment() throws Exception {//1.判断while (number != 0) {this.wait();}//2.⼲活number++;System.out.println(Thread.currentThread().getName() + "\t" + number);//3通知this.notifyAll();}public synchronized void decrement() throws Exception {//1.判断while (number == 0) {this.wait();}//2.⼲活number--;System.out.println(Thread.currentThread().getName() + "\t" + number);//3通知this.notifyAll();}
}

2.2.⽣产者消费者防⽌虚假唤醒

2.2.1 新版⽣产者消费者写法 ReentrantLock.Condition

Synchronized用在多线程中太重了,在高并发场景使用lock方式更加合适。所以我们使用lock来加锁和解锁。对应的等待和唤醒线程方法也换成java.util.concurrent.locks下面的newCondition方法。
在这里插入图片描述

案例代码

class Aircondition{private int number = 0;private Lock lock = new ReentrantLock();private Condition condition = lock.newCondition();//新版写法public void increment() throws Exception{lock.lock();try{//1.判断while (number != 0){condition.await();}//2.干活number++;System.out.println(Thread.currentThread().getName()+"\t"+number);//3通知condition.signalAll();} catch (Exception e) {e.printStackTrace();} finally {lock.unlock();}}public void decrement() throws Exception{lock.lock();try{//1.判断while (number == 0){condition.await();}//2.干活number--;System.out.println(Thread.currentThread().getName()+"\t"+number);//3通知condition.signalAll();} catch (Exception e) {e.printStackTrace();} finally {lock.unlock();}}
}public class ProdConsumerDemo {public static void main(String[] args) {Aircondition aircondition = new Aircondition();new Thread(() -> {for (int i = 1; i <= 10; i++) {try {aircondition.increment();} catch (Exception e) {e.printStackTrace();}}}, "A").start();new Thread(() -> {for (int i = 1; i <= 5; i++) {try {aircondition.decrement();} catch (Exception e) {e.printStackTrace();}}}, "B").start();new Thread(() -> {for (int i = 1; i <= 10; i++) {try {aircondition.increment();} catch (Exception e) {e.printStackTrace();}}}, "C").start();new Thread(() -> {for (int i = 1; i <= 10; i++) {try {aircondition.decrement();} catch (Exception e) {e.printStackTrace();}}}, "D").start();}
}

2.2.2精准通知顺序访问

现在有这样一个需求:

备注:多线程之间按顺序调用,实现A->B->C
三个线程启动,要求如下:
A打印5次,B打印10次,C打印15次
接着
A打印5次,B打印10次,C打印15次
来10轮
1.高内聚低耦合前提下,线程操作资源类
2.判断/干活/通知
3.多线程交互中,防止虚假唤醒(判断只能用while,不能用if)
4.标志位

在这里插入图片描述

案例代码

class ShareData{private int number = 1;//A:1,B:2,C:3private Lock lock = new ReentrantLock();private Condition c1 = lock.newCondition();private Condition c2 = lock.newCondition();private Condition c3 = lock.newCondition();public void printc1(){lock.lock();try {//1.判断while (number != 1){c1.await();}//2.干活for (int i = 1; i <= 5; i++) {System.out.println(Thread.currentThread().getName()+"\t"+i);}//3.通知number = 2;//通知第2个c2.signal();} catch (Exception e) {e.printStackTrace();} finally {lock.unlock();}}public void printc2(){lock.lock();try {//1.判断while (number != 2){c2.await();}//2.干活for (int i = 1; i <= 10; i++) {System.out.println(Thread.currentThread().getName()+"\t"+i);}//3.通知number = 3;//如何通知第3个c3.signal();} catch (Exception e) {e.printStackTrace();} finally {lock.unlock();}}public void printc3(){lock.lock();try {//1.判断while (number != 3){c3.await();}//2.干活for (int i = 1; i <= 15; i++) {System.out.println(Thread.currentThread().getName()+"\t"+i);}//3.通知number = 1;//如何通知第1个c1.signal();} catch (Exception e) {e.printStackTrace();} finally {lock.unlock();}}
}/*** 备注:多线程之间按顺序调用,实现A->B->C* 三个线程启动,要求如下:* A打印5次,B打印10次,C打印15次* 接着* A打印5次,B打印10次,C打印15次* 来10轮*      1.高内聚低耦合前提下,线程操作资源类*      2.判断/干活/通知*      3.多线程交互中,防止虚假唤醒(判断只能用while,不能用if)*      4.标志位*/
public class ConditionDemo {public static void main(String[] args) {ShareData shareData = new ShareData();new Thread(()->{for (int i = 1; i <= 10; i++) {shareData.printc1();}},"A").start();new Thread(()->{for (int i = 1; i <= 10; i++) {shareData.printc2();}},"B").start();new Thread(()->{for (int i = 1; i <= 10; i++) {shareData.printc3();}},"C").start();}
}

结果
在这里插入图片描述
结果分析
在这里插入图片描述

2.3.Synchronized和Lock的区别

synchronized关键字和 java.util.concurrent.locks.Lock都能加锁,两者有什么区别呢?

  1. 原始构成: sync是JVM层⾯的,底层通过monitorenter和monitorexit来实现的。 Lock是
    JDK API层⾯的。( sync⼀个enter会有两个exit,⼀个是正常退出,⼀个是异常退出)
  2. 使⽤⽅法: sync不需要⼿动释放锁,⽽ Lock需要⼿动释放。
  3. 是否可中断: sync不可中断,除⾮抛出异常或者正常运⾏完成。 Lock是可中断的,通过调
    ⽤ interrupt()⽅法。
  4. 是否为公平锁: sync只能是⾮公平锁,⽽ Lock既能是公平锁,⼜能是⾮公平锁。
  5. 绑定多个条件: sync不能,只能随机唤醒。⽽ Lock可以通过 Condition来绑定多个条件,精确唤醒。

2.4. 阻塞队列模式⽣产者消费者

为什么需要BlockingQueue?

好处是我们不需要关⼼什么时候需要阻塞线程,什么时候需要唤醒线程,因为这⼀切BlockingQueue都
给你⼀⼿包办好了,使⽤阻塞队列 后就不需要⼿动加锁了。
在Concurrent包发布以前,在多线程环境下,我们每个程序员都必须去⾃⼰控制这些细节,尤其还要兼
顾效率和线程安全,⽽这会给我们的程序带来不⼩的复杂度。

在这里插入图片描述

案例代码

public class ProdConsBlockQueueDemo {public static void main(String[] args) {MyResource myResource = new MyResource(new ArrayBlockingQueue<>(5));new Thread(() -> {System.out.println(Thread.currentThread().getName() + "\t生产线程启动");try {myResource.myProd();} catch (Exception e) {e.printStackTrace();}}, "prod").start();new Thread(() -> {System.out.println(Thread.currentThread().getName() + "\t生产线程启动");try {myResource.myProd();} catch (Exception e) {e.printStackTrace();}}, "prod-2").start();new Thread(() -> {System.out.println(Thread.currentThread().getName() + "\t消费线程启动");try {myResource.myCons();} catch (Exception e) {e.printStackTrace();}}, "cons").start();new Thread(() -> {System.out.println(Thread.currentThread().getName() + "\t消费线程启动");try {myResource.myCons();} catch (Exception e) {e.printStackTrace();}}, "cons-2").start();try {TimeUnit.SECONDS.sleep(5);} catch (Exception e) {e.printStackTrace();}System.out.println("5秒钟后,叫停");myResource.stop();}
}class MyResource {// 定义成volatile类型,线程间可见 默认开启,进行生产+消费private volatile boolean FLAG = true;//定义原子Integer类型保证原子性private AtomicInteger atomicInteger = new AtomicInteger();private BlockingQueue<String> blockingQueue;public MyResource(BlockingQueue<String> blockingQueue) {this.blockingQueue = blockingQueue;System.out.println(blockingQueue.getClass().getName());}public void myProd() throws Exception {String data = null;boolean retValue;while (FLAG) {data = atomicInteger.incrementAndGet() + "";//++iretValue = blockingQueue.offer(data, 2L, TimeUnit.SECONDS);if (retValue) {System.out.println(Thread.currentThread().getName() + "\t" + "插入队列" + data + "成功");} else {System.out.println(Thread.currentThread().getName() + "\t" + "插入队列" + data + "失败");}TimeUnit.SECONDS.sleep(1);}System.out.println(Thread.currentThread().getName() + "\t老板叫停了,FLAG已更新为false,停止生产");}public void myCons() throws Exception {String res;while (FLAG) {res = blockingQueue.poll(2L, TimeUnit.SECONDS);if (null == res || "".equals(res)) {// FLAG = false;System.out.println(Thread.currentThread().getName() + "\t超过2秒钟没有消费,退出消费");return;}System.out.println(Thread.currentThread().getName() + "\t\t消费队列" + res + "成功");}}public void stop() {this.FLAG = false;}
}

结果分析
在这里插入图片描述

相关文章:

并发编程---阻塞队列(五)

阻塞队列一 阻塞队列1.1.阻塞队列概念1.2.阻塞队列API案例1.2.1. ArrayBlockingQueue1.2.1.1.抛出异常1.2.1.2.返回布尔1.2.1.3.阻塞1.2.1.4.超时1.2.2.SynchronousQueue二 阻塞队列应用---生产者消费者2.1.传统模式案例代码结果案例问题---防止虚假唤醒2.2.⽣产者消费者防⽌虚…...

本科课程【计算机组成原理】实验1 - 输出ABCD程序的生成

大家好,我是【1+1=王】, 热爱java的计算机(人工智能)渣硕研究生在读。 如果你也对java、人工智能等技术感兴趣,欢迎关注,抱团交流进大厂!!! Good better best, never let it rest, until good is better, and better best. 近期会把自己本科阶段的一些课程设计、实验报…...

Java并发编程(2) —— 线程创建的方式与原理

一、Java线程创建的三种方式 1. 继承Thread类并重写run()方法 ///方法一&#xff1a;使用匿名内部类重写Thread的run()方法Thread t1 new Thread() {Overridepublic void run() {try {sleep(10000);} catch (InterruptedException e) {e.printStackTrace();}log.debug("…...

你写的js性能有多差你知道吗 | js性能优化

性能的计算⽅式 确认⾃⼰需要关注的指标 常⻅的指标有&#xff1a; ⻚⾯总加载时间 load⾸屏时间⽩屏时间 代码 尝试⽤⼀个指令, 挂载在重要元素上, 当此元素inserted就上报 各个属性所代表的含义 connectStart, connectEnd 分别代表TCP建⽴连接和连接成功的时间节点。如果浏…...

线程的状态、状态之间的相互转换

目录 一、线程的状态 1. NEW 2. TERMINATED 3. RUNNABLE 4. TIMED_WAITING 5. BLOCKED 6. WAITING 二、线程状态转换 1. 线程状态转换简图 一、线程的状态 线程的状态一共有 6 种&#xff1a; NEW&#xff1a;安排了工作&#xff0c;还未开始行动&#xff08;调用 st…...

Java8使用Lambda表达式(流式)快速实现List转map 、分组、过滤等操作

利用java8新特性&#xff0c;可以用简洁高效的代码来实现一些数据处理。1 数据准备1.1 定义1个Fruit对象package com.wkf.workrecord.work;import org.junit.Test;import java.math.BigDecimal; import java.util.ArrayList; import java.util.List;/*** author wuKeFan* date …...

C++之深浅拷贝

一、浅拷贝 我们看下以下代码 Test.h 文件 #pragma once #include<iostream> using namespace std; class Student { public:Student(){}~Student(){if (m_Id ! nullptr){delete m_Id;m_Id nullptr;}}Student(int id, string strName){m_Id new int[id];m_strName s…...

CoreLocation的一切

Overview 概述 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pgnvehxf-1678717852996)(./blog_cover.png)] Core Location 提供的服务可以确定设备的地理位置、高度和方向&#xff0c;或者它相对于附近 iBeacon 设备的位置。 该框架使用设备上的所…...

HashMap原理

初始化 从HashMap 源码中我们可以发现&#xff0c;HashMap的初始化有一下四种方式 //HashMap默认的初始容量大小 16&#xff0c;容量必须是2的幂 static final int DEFAULT_INITIAL_CAPACITY 1 << 4; // HashMap最大容量 static final int MAXIMUM_CAPACITY 1 <&…...

STM32入门笔记(02):独立看门狗(IWDG)和窗户看门狗(WWDG)(SPL库函数版)

1.IWDG狗简介 除了始终控制器的RCC_CSR寄存器的父为标志位和备份区域中的寄存器以外&#xff0c;系统复位 将复位所有寄存器至它们的复位状态。 当发生以下任一事件时&#xff0c;产生一个系统复位&#xff1a; 1.NRST引脚上的 低 电平&#xff0c;即 外部复位&#xff1b;2…...

javaSE系列之方法与数组的使用

[TOC] javaSE系列之方法与数组的使用 方法的定义 方法类似于C语言中的"函数"。 方法的种类 这里方法分为有参方法也分为无参方法&#xff0c; 形参和实参是两个实体&#xff08;这里相当于函数的传值调用和传址调用&#xff09; 1.非静态方法&#xff1a;普通方法/…...

常用命令总结

将常用命令汇集于此&#xff0c;以便在忘记的时候查询&#xff0c;持续更新…… Git Local changes 添加名字&#xff1a; git config --global user.name "<你的名字>"添加邮件&#xff1a; git config --globa user.email "<你的邮箱>"…...

【Linux:程序地址空间--原来操作系统也喜欢画大饼】

目录 1 代码感受 2 进程地址空间 3 扩展 1 代码感受 在正式讲程序地址空间前我们先来看一段简单的代码来分析分析&#xff1a; 1 #include<iostream>2 #include<unistd.h>3 using namespace std;4 5 int g_val100;6 7 int main()8 {9 pid_t idfork();10 if(i…...

Python实现简单信号滤波实战

在有些项目中需要对信号进行滤波处理&#xff0c;尤其是在医疗的设备中如心跳、脉搏等设备的采样后进行处理。滤波的目的就是除去某些频率的信号如噪声。常见的包括有低通滤波、高通滤波、带通滤波。 低通滤波指的是去除高于某一阈值频率的信号&#xff1b;高通滤波去除低于某…...

Java(110):非对称加密RSA的使用(KeyPair生成密钥)

Java(110)&#xff1a;非对称加密RSA的使用(KeyPair生成密钥) RSA 算法是一种非对称加解密算法。服务方生成一对 RSA 密钥&#xff0c;即公钥 私钥&#xff0c;将公钥提供给调用方&#xff0c;调用方使用公钥对数据进行加密后&#xff0c;服务方根据私钥进行解密。 1、RSA生…...

(Mybatis 学习【1】)整合 Mybatis 开发流程

Mybatis 整合流程 ① 添加MyBatis的依赖 ② 创建数据库表 ③ 编写pojo实体类 ④ 编写映射文件UserMapper.xml ⑤ 编写核心文件mybatis-config.xml ⑥ 编写测试类** 编写 pojo 实体类 (设计相应的数据库&#xff09; Data AllArgsConstructor NoArgsConstructor public class…...

一文搞懂Kerberos

Kerberos一词来源于古希腊神话中的Cerberus——守护地狱之门的三头犬&#xff0c;Kerberos是为TCP/IP 网络设计的可信第三方鉴别协议&#xff0c;最初是在麻省理工学院(MIT)为Athena 项目而开发的。Kerberos服务起着可信仲裁者的作用&#xff0c;可提供安全的网络鉴别&#xff…...

Go爬虫学习笔记(三)

day3 ‍ 04&#xff5c;敏捷之道&#xff1a;大型Go项目的开发流程是怎样的&#xff1f; 瀑布模式 流程&#xff1a; 市场调研需求分析产品设计研发实现集成与测试项目交付与维护 适用场景&#xff1a; 需求在规划和设计阶段就已经确定了&#xff0c;而且在项目开发周期内&…...

CASTEP参数设置(2)

虚拟试验&#xff08;分子模拟&#xff09; 在表征材料以及材料的相关性质时&#xff0c;只要是采用已有的理论加以解释 但是通常来说&#xff0c;需要采用已有的理论来进行设计和探索&#xff0c;伴随着工业软件的发展&#xff0c;应当选用仿真技术来缩小探索范围 传统试验V…...

浅谈对Promise的理解以及在工作中的应用

浅谈对Promise的理解以及在工作中的应用Promise的概念背景知识JavaScript的同步和异步JavaScript事件循环回调函数进行异步操作解决方案&#xff1a;PromisePromise 在工作中的运用创建PromisePromise封装AJAXPromise链式操作Promise.all()Promise.race()async和await总结Promi…...

关于nvm与node.js

1 安装nvm 安装过程中手动修改 nvm的安装路径&#xff0c; 以及修改 通过nvm安装node后正在使用的node的存放目录【这句话可能难以理解&#xff0c;但接着往下看你就了然了】 2 修改nvm中settings.txt文件配置 nvm安装成功后&#xff0c;通常在该文件中会出现以下配置&…...

Go 语言接口详解

Go 语言接口详解 核心概念 接口定义 在 Go 语言中&#xff0c;接口是一种抽象类型&#xff0c;它定义了一组方法的集合&#xff1a; // 定义接口 type Shape interface {Area() float64Perimeter() float64 } 接口实现 Go 接口的实现是隐式的&#xff1a; // 矩形结构体…...

【第二十一章 SDIO接口(SDIO)】

第二十一章 SDIO接口 目录 第二十一章 SDIO接口(SDIO) 1 SDIO 主要功能 2 SDIO 总线拓扑 3 SDIO 功能描述 3.1 SDIO 适配器 3.2 SDIOAHB 接口 4 卡功能描述 4.1 卡识别模式 4.2 卡复位 4.3 操作电压范围确认 4.4 卡识别过程 4.5 写数据块 4.6 读数据块 4.7 数据流…...

Hive 存储格式深度解析:从 TextFile 到 ORC,如何选对数据存储方案?

在大数据处理领域&#xff0c;Hive 作为 Hadoop 生态中重要的数据仓库工具&#xff0c;其存储格式的选择直接影响数据存储成本、查询效率和计算资源消耗。面对 TextFile、SequenceFile、Parquet、RCFile、ORC 等多种存储格式&#xff0c;很多开发者常常陷入选择困境。本文将从底…...

免费数学几何作图web平台

光锐软件免费数学工具&#xff0c;maths,数学制图&#xff0c;数学作图&#xff0c;几何作图&#xff0c;几何&#xff0c;AR开发,AR教育,增强现实,软件公司,XR,MR,VR,虚拟仿真,虚拟现实,混合现实,教育科技产品,职业模拟培训,高保真VR场景,结构互动课件,元宇宙http://xaglare.c…...

数学建模-滑翔伞伞翼面积的设计,运动状态计算和优化 !

我们考虑滑翔伞的伞翼面积设计问题以及运动状态描述。滑翔伞的性能主要取决于伞翼面积、气动特性以及飞行员的重量。我们的目标是建立数学模型来描述滑翔伞的运动状态,并优化伞翼面积的设计。 一、问题分析 滑翔伞在飞行过程中受到重力、升力和阻力的作用。升力和阻力与伞翼面…...

6个月Python学习计划 Day 16 - 面向对象编程(OOP)基础

第三周 Day 3 &#x1f3af; 今日目标 理解类&#xff08;class&#xff09;和对象&#xff08;object&#xff09;的关系学会定义类的属性、方法和构造函数&#xff08;init&#xff09;掌握对象的创建与使用初识封装、继承和多态的基本概念&#xff08;预告&#xff09; &a…...

土建施工员考试:建筑施工技术重点知识有哪些?

《管理实务》是土建施工员考试中侧重实操应用与管理能力的科目&#xff0c;核心考查施工组织、质量安全、进度成本等现场管理要点。以下是结合考试大纲与高频考点整理的重点内容&#xff0c;附学习方向和应试技巧&#xff1a; 一、施工组织与进度管理 核心目标&#xff1a; 规…...

MySQL体系架构解析(三):MySQL目录与启动配置全解析

MySQL中的目录和文件 bin目录 在 MySQL 的安装目录下有一个特别重要的 bin 目录&#xff0c;这个目录下存放着许多可执行文件。与其他系统的可执行文件类似&#xff0c;这些可执行文件都是与服务器和客户端程序相关的。 启动MySQL服务器程序 在 UNIX 系统中&#xff0c;用…...

【Java多线程从青铜到王者】单例设计模式(八)

wait和sleep的区别 我们的wait也是提供了一个还有超时时间的版本&#xff0c;sleep也是可以指定时间的&#xff0c;也就是说时间一到就会解除阻塞&#xff0c;继续执行 wait和sleep都能被提前唤醒(虽然时间还没有到也可以提前唤醒)&#xff0c;wait能被notify提前唤醒&#xf…...