当前位置: 首页 > 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…...

【杂谈】-递归进化:人工智能的自我改进与监管挑战

递归进化&#xff1a;人工智能的自我改进与监管挑战 文章目录 递归进化&#xff1a;人工智能的自我改进与监管挑战1、自我改进型人工智能的崛起2、人工智能如何挑战人类监管&#xff1f;3、确保人工智能受控的策略4、人类在人工智能发展中的角色5、平衡自主性与控制力6、总结与…...

ESP32读取DHT11温湿度数据

芯片&#xff1a;ESP32 环境&#xff1a;Arduino 一、安装DHT11传感器库 红框的库&#xff0c;别安装错了 二、代码 注意&#xff0c;DATA口要连接在D15上 #include "DHT.h" // 包含DHT库#define DHTPIN 15 // 定义DHT11数据引脚连接到ESP32的GPIO15 #define D…...

[ICLR 2022]How Much Can CLIP Benefit Vision-and-Language Tasks?

论文网址&#xff1a;pdf 英文是纯手打的&#xff01;论文原文的summarizing and paraphrasing。可能会出现难以避免的拼写错误和语法错误&#xff0c;若有发现欢迎评论指正&#xff01;文章偏向于笔记&#xff0c;谨慎食用 目录 1. 心得 2. 论文逐段精读 2.1. Abstract 2…...

CocosCreator 之 JavaScript/TypeScript和Java的相互交互

引擎版本&#xff1a; 3.8.1 语言&#xff1a; JavaScript/TypeScript、C、Java 环境&#xff1a;Window 参考&#xff1a;Java原生反射机制 您好&#xff0c;我是鹤九日&#xff01; 回顾 在上篇文章中&#xff1a;CocosCreator Android项目接入UnityAds 广告SDK。 我们简单讲…...

现代密码学 | 椭圆曲线密码学—附py代码

Elliptic Curve Cryptography 椭圆曲线密码学&#xff08;ECC&#xff09;是一种基于有限域上椭圆曲线数学特性的公钥加密技术。其核心原理涉及椭圆曲线的代数性质、离散对数问题以及有限域上的运算。 椭圆曲线密码学是多种数字签名算法的基础&#xff0c;例如椭圆曲线数字签…...

安宝特方案丨船舶智造的“AR+AI+作业标准化管理解决方案”(装配)

船舶制造装配管理现状&#xff1a;装配工作依赖人工经验&#xff0c;装配工人凭借长期实践积累的操作技巧完成零部件组装。企业通常制定了装配作业指导书&#xff0c;但在实际执行中&#xff0c;工人对指导书的理解和遵循程度参差不齐。 船舶装配过程中的挑战与需求 挑战 (1…...

七、数据库的完整性

七、数据库的完整性 主要内容 7.1 数据库的完整性概述 7.2 实体完整性 7.3 参照完整性 7.4 用户定义的完整性 7.5 触发器 7.6 SQL Server中数据库完整性的实现 7.7 小结 7.1 数据库的完整性概述 数据库完整性的含义 正确性 指数据的合法性 有效性 指数据是否属于所定…...

TSN交换机正在重构工业网络,PROFINET和EtherCAT会被取代吗?

在工业自动化持续演进的今天&#xff0c;通信网络的角色正变得愈发关键。 2025年6月6日&#xff0c;为期三天的华南国际工业博览会在深圳国际会展中心&#xff08;宝安&#xff09;圆满落幕。作为国内工业通信领域的技术型企业&#xff0c;光路科技&#xff08;Fiberroad&…...

消息队列系统设计与实践全解析

文章目录 &#x1f680; 消息队列系统设计与实践全解析&#x1f50d; 一、消息队列选型1.1 业务场景匹配矩阵1.2 吞吐量/延迟/可靠性权衡&#x1f4a1; 权衡决策框架 1.3 运维复杂度评估&#x1f527; 运维成本降低策略 &#x1f3d7;️ 二、典型架构设计2.1 分布式事务最终一致…...

pgsql:还原数据库后出现重复序列导致“more than one owned sequence found“报错问题的解决

问题&#xff1a; pgsql数据库通过备份数据库文件进行还原时&#xff0c;如果表中有自增序列&#xff0c;还原后可能会出现重复的序列&#xff0c;此时若向表中插入新行时会出现“more than one owned sequence found”的报错提示。 点击菜单“其它”-》“序列”&#xff0c;…...