JavaEE-经典多线程样例

文章目录

  • 单例模式
    • 设计模式初步引入
    • 为何存在单例模式
    • 饿汉式单例模式
    • 饿汉式缺陷以及是否线程安全
    • 懒汉式单例模式
    • 基础懒汉式缺陷以及是否线程安全
    • 懒汉式单例模式的改进
    • 完整代码(变量volatile)
  • 阻塞队列
    • 生产者消费者模型
    • 生产者消费者模型的案例以及优点
      • 请求与响应案例
      • 解耦合
      • 削峰填谷
    • 阻塞队列的内置API
    • 阻塞队列的模拟实现
      • 关于wait和while的搭配使用
      • 模拟实现

单例模式

设计模式初步引入

啥是设计模式?

  • 设计模式好⽐象棋中的 “棋谱”. 红⽅当头炮, ⿊⽅⻢来跳. 针对红⽅的⼀些⾛法, ⿊⽅应招的时候有⼀些固定的套路. 按照套路来⾛局势就不会吃亏.软件开发中也有很多常⻅的 “问题场景”. 针对这些问题场景, ⼤佬们总结出了⼀些固定的套路. 按照这个套路来实现代码, 也不会吃亏, 不针对某一种语言, 而是针对某种开发场景
  • 设计模式并不是只有23种, 因为之前有些大佬写了一本书叫设计模式,重点讨论了23种, 但事实上存在更多种的设计模式
  • 设计模式与框架的区别就是, 设计模式在开发中是软性要求(不一定遵守), 但是框架是硬性要求(一定要遵守)

简单点一句话总结

设计模式是前人根据一些开发场景给出的一些经验之谈, 所以设计模式并不针对某一种语言

为何存在单例模式

  • 单例模式能保证某个类在程序中只存在唯⼀⼀份实例, ⽽不会创建出多个实例.
    这⼀点在很多场景上都需要. 比如 JDBC 中的 DataSource 实例就只需要⼀个,再
    比如如果一个类的创建需要加载的数据量非常的庞大(GB级别), 那我们不希望这
    个类频繁的创建销毁(开销很大), 我们可能只是希望创建一次就可以了

饿汉式单例模式

顾名思义, 这种方式实现的单例模式十分"饥渴", 不管使用不使用都会提前new一个对象

流程如下

  • 构造方法私有化
  • 定义一个静态的类对象用以返回
  • 提供一个公开的静态接口来获取唯一的对象

测试代码如下

/*** 下面定义一个类来测试饿汉式单例模式*/
class HungrySingleton{// 提供一个静态的变量用来返回private static HungrySingleton hungrySingleton = new HungrySingleton();// 构造方法私有化(在外部不可以构造对象)private HungrySingleton(){}// 提供一个获取实例的静态公开接口public static HungrySingleton getInstance(){return hungrySingleton;}
}public class DesignPatternTest {public static void main(String[] args) {// 对饿汉式单例的测试HungrySingleton instance1 = HungrySingleton.getInstance();HungrySingleton instance2 = HungrySingleton.getInstance();// 测试两者是不是一个对象System.out.println(instance1 == instance2);}
}

测试结果
在这里插入图片描述
很明显, 用这种方式创建的实例都是只有一份的…

饿汉式缺陷以及是否线程安全

首先饿汉式的单例模式缺陷是非常明显的

  • 饿汉式不管我们使用这个对象与否, 都会在类加载的时期(因为是静态对象)构建一个这样的对象, 但我们想要达成的效果是, 在我们不需要这种类的实例的时候, 我们不去进行构造对象的操作(变主动为被动)来减少内存等相关资源的开销

但是饿汉式单例一定是线程安全的

  • 构建对象的时期是类加载的时候, 后期不同线程对于这个实例的操作也仅仅是涉及到读操作, 不涉及修改操作, 所以当然是线程安全的, 不存在线程安全问题, 但是另一种实现的模式就不一定了

懒汉式单例模式

上面说了饿汉式单例模式的缺陷, 我们尝试使用懒汉式单例的方式去解决这个问题, 也就是仅仅在需要的时候进行new对象的操作

最基础的懒汉单例模式

构造的逻辑

  • 构造方法私有化
  • 提供一个静态的对象用来返回(暂时不new对象)
  • 提供一个公开访问的静态接口来返回唯一的对象

代码测试(最基础的版本)

/*** 下面定义一个类来测试懒汉式单例模式*/
class LazySingleton{// 提供一个静态的变量用来返回private static LazySingleton lazySingleton = null;// 构造方法私有化(不可以在外部new对象)private LazySingleton(){}// 提供一个公开的获取实例的接口public static LazySingleton getInstance(){if(lazySingleton == null){lazySingleton = new LazySingleton();}return lazySingleton;}
}public class DesignPatternTest {public static void main(String[] args) {// 对懒汉式单例的测试LazySingleton instance1 = LazySingleton.getInstance();LazySingleton instance2 = LazySingleton.getInstance();// 测试两者是不是一个对象System.out.println(instance1 == instance2);}
}

基础懒汉式缺陷以及是否线程安全

这个就和上面饿汉有较大的区别了, 虽然解决了在需要的时候进行new对象, 上面的基础版本的懒汉式在单线程的环境下肯定是没问题的, 但是在多线程的环境下就不好说了…看下面的分析

如果在多线程的环境下(我们假设有t1, t2)是下图的执行顺序
在这里插入图片描述
很明显这是一种类似串行的执行策略

但是还可能是下图的情况
在这里插入图片描述
t1线程判断完毕之后没有来得及进行new对象, t2线程紧接着进行了一次完整的new对象的过程, 此时t1线程又进行了一次new对象的过程, 很明显, 我们上面的情况进行了两次构造对象的过程, 同时拿到的对象也不一致

我们通过Thread.sleep()的方式进行延迟观察看是否会发生

/*** 下面定义一个类来测试懒汉式单例模式*/
class LazySingleton{// 提供一个静态的变量用来返回private static LazySingleton lazySingleton = null;// 构造方法私有化(不可以在外部new对象)private LazySingleton(){}// 提供一个公开的获取实例的接口public static LazySingleton getInstance() throws InterruptedException {if(lazySingleton == null){Thread.sleep(1000);lazySingleton = new LazySingleton();}return lazySingleton;}
}public class DesignPatternTest {private static LazySingleton instance1 = null;private static LazySingleton instance2 = null;public static void main(String[] args) throws InterruptedException {// 创建两个线程获取实例Thread t1 = new Thread(() -> {try {instance1 = LazySingleton.getInstance();} catch (InterruptedException e) {e.printStackTrace();}});Thread t2 = new Thread(() -> {try {instance2 = LazySingleton.getInstance();} catch (InterruptedException e) {e.printStackTrace();}});// 开启两个线程t1.start();t2.start();// 睡眠等待一下Thread.sleep(2000);System.out.println(instance1 == instance2);}
}

在这里插入图片描述
很明显, 这样的懒汉式的代码是线程不安全的, 那要如何进行改进呢???

懒汉式单例模式的改进

之前我们说了, 要想保证线程是安全的, 有几种解决方式, 这里面我们就采取加锁, 因为其实
判断是不是null和对象应该是一个整体的原子性的操作

改进之后的代码

/*** 下面定义一个类来测试懒汉式单例模式(改进版)*/
class LazySingleton{// 提供一个静态的变量用来返回private static LazySingleton lazySingleton = null;// 构造方法私有化(不可以在外部new对象)private LazySingleton(){}// 提供一个公开的获取实例的接口public static LazySingleton getInstance() {// 我们把if判断和new对象通过加锁打包为一个原子性的操作(这里使用类对象锁)synchronized (LazySingleton.class){if(lazySingleton == null){lazySingleton = new LazySingleton();}}return lazySingleton;}
}public class DesignPatternTest {private static LazySingleton instance1 = null;private static LazySingleton instance2 = null;public static void main(String[] args) {// 在多线程中获取实例Thread t1 = new Thread(() -> {instance1 = LazySingleton.getInstance();});Thread t2 = new Thread(() -> {instance2 = LazySingleton.getInstance();});System.out.println(instance1 == instance2);}
}

这时候肯定是一个线程安全的代码了, 但是思考可不可以进一步改进呢???


当我们已经new个一次对象之后, 如果后续的线程想要获取这个对象, 那就仅仅是一个操作了, 根本不涉及对对象的修改, 但是我们每次都使用锁这样的机制就会造成阻塞, 也就会导致程序的效率下降, 所以我们对代码进行了下面的修改在外层再加一个if判断

改进的方法如下

// 提供一个公开的获取实例的接口public static LazySingleton getInstance() {// 我们把if判断和new对象通过加锁打包为一个原子性的操作(这里使用类对象锁)if (lazySingleton == null) {synchronized (LazySingleton.class) {if (lazySingleton == null) {lazySingleton = new LazySingleton();}}}return lazySingleton;}

我们的两个if的含义

  • 第一个if: 判断对象是否创建完毕, 如果创建了, 只是一个读操作
  • 第二个if: 判断是不是需要new对象

可能初学多线程的时候, 看上述代码觉得很迷惑, 但其实这是因为之前我们写的程序都是单线程的情况, 单线程中执行流只有一个, 两次相同的if判断其实是没有必要的, 但是多线程的条件下, 是多个执行流, 相同的逻辑判断条件也可能产生不同的结果

完整代码(变量volatile)

关于变量是否会产生指令重排序和内存可见性问题, 我们直接加上volatile即可

/*** 下面定义一个类来测试懒汉式单例模式(完整改进版)*/
class LazySingleton {// 提供一个静态的变量用来返回private volatile static LazySingleton lazySingleton = null;// 构造方法私有化(不可以在外部new对象)private LazySingleton() {}// 提供一个公开的获取实例的接口public static LazySingleton getInstance() {// 我们把if判断和new对象通过加锁打包为一个原子性的操作(这里使用类对象锁)if (lazySingleton == null) {synchronized (LazySingleton.class) {if (lazySingleton == null) {lazySingleton = new LazySingleton();}}}return lazySingleton;}
}

阻塞队列

生产者消费者模型

关于生产者消费者模型, 其实是生活中抽象出来的一个模型案例, 我们举一个包饺子的例子来简单解释一下

  • 在包饺子的过程中, 存在一个擀饺子皮的人, 我们称之为生产者, 擀出来的饺子皮放到一个竹盘上, 这个竹盘相当于一个中间的媒介, 生产者生产的物质在上面与消费者进行交互, 而包饺子的人就是一个消费者, 从中间媒介中取出东西, 也就是消费的过程, 我们的中间的竹盘相当于一个缓冲, 如果包饺子的人包的快的话, 就需要等待做饺子皮的人, 如果做饺子皮的人做的快的话, 当竹盘放不下的时候就需要阻塞等待

  • 上面的情景抽象成生产者消费者模型, 擀饺子皮的人是生产者, 竹盖是阻塞队列, 包饺子的人是消费者

生产者消费者模型的案例以及优点

请求与响应案例

生产者消费者模型我们举一个"请求响应的案例"

在这里插入图片描述
图中我们也有解释, 越靠上游的消耗的资源越少
假设我们现在出现一个秒杀的请求, 上游可能还可以运行, 但是下游的服务器由于并发量过大就直接崩溃了

在这里插入图片描述
所以我们一般会对上面提供服务的逻辑进行改变
添加一个中间的结构(阻塞队列, 或者说消息队列)进行缓冲

在这里插入图片描述


在真实的开发场景当中, 阻塞队列甚至会单独的部署为一台服务器, 这种独立的服务器结构叫做消息队列, 可见其重要性


解耦合

生产者消费者模型的一个重要的优点就是让消费者和生产者解耦合

  • 根据上面的模型分析, 不管是生产者还是消费者都是面向阻塞队列来进行任务的执行的, 所以就降低了两者之间的耦合度, 将来想要修改这个模型的工作内容, 也只需要面向阻塞队列操作更改(其实相当于接口), 如果没有这种机制的话, 我们想要更改一个操作逻辑, 就需要同时修改消费者与生产者的代码结构…, 我们先前学习的接口其实就是一种解耦合的策略, 其核心就是减少耦合度, 便于对代码结构进行调整

削峰填谷

刚才我们的那个模型就说了, 如果消息请求量非常大的时候, 如果没有消息队列的存在, 就会对下游的服务器产生较大的影响, 甚至会导致服务器崩溃


下图是正常情况下消息队列的工作示意图, 添加的任务加入消息队列, 然后下游的服务器以一个相对稳定的效率从队列中取出来任务进行处理
在这里插入图片描述


下图是当任务量激增的时候, 虽然任务量激增, 但是依旧进入消息队列进行等待处理, 此时下游的服务器对任务的处理的效率基本不变, 所以可以保证处理的稳定性, 不至于让下游服务器崩溃, 因为这个消息一般都是一阵一阵的激增, 所以等到下一轮消息量减少的时候, 对先前消息队列的数据进行清理即可…
在这里插入图片描述

阻塞队列的内置API

下图是我们相关的阻塞队列的内置API继承逻辑

在这里插入图片描述


关于构造方法

ArrayBlockingQueue: 必须指定大小
LinkedBlockQueue: 可以指定也可以不指定


关于offerpollputtake的区别

首先是offerpoll

在这里插入图片描述
这两个方法也可以使阻塞队列产生阻塞的效果, 但是我们可以指定一个最大的等待时间
我们使用下面的代码测试

/*** 关于阻塞队列的相关测试*/
public class ThreadTest {public static void main(String[] args) {// 生成一个阻塞队列(指定队列的大小为100)BlockingQueue<Integer> blockingQueue = new ArrayBlockingQueue<Integer>(100);// 创建两个线程测试Thread producer = new Thread(() -> {for(int i = 0; i < 1000; i++){try {blockingQueue.offer(i, 10L, TimeUnit.SECONDS);System.out.println("生产了元素: " + i);} catch (InterruptedException e) {e.printStackTrace();}}});// 消费者线程Thread consumer = new Thread(() -> {while(true){try {// 进行休眠Thread.sleep(1000 * 1);int elem = blockingQueue.take();System.out.println("消费了元素: " + elem);} catch (InterruptedException e) {e.printStackTrace();}}});producer.start();consumer.start();}
}

分析下这个程序的执行的逻辑

  • 在程序启动的很短的时间内, 由于阻塞队列的容量还有空余, 所以会大量的生产元素直到阻塞队列满了, 因为消费者线程是每一秒钟消耗一个元素, 所以存在等待时间, 我们上述代码设置的最大的等待时间是10s, 所以根本来不及等待到最大的时间点就可以进行取出元素…

puttake方法

  • 这组方法和上组方法的区别就是, 这个方法是当队列满或者队列空, 我们进行无限期的阻塞…, 直到队列中的元素不为空或者不为满就可以进行操作
/*** 关于阻塞队列的相关测试*/
public class ThreadTest {public static void main(String[] args) {// 生成一个阻塞队列(指定队列的大小为100)BlockingQueue<Integer> blockingQueue = new ArrayBlockingQueue<Integer>(100);// 创建两个线程测试Thread producer = new Thread(() -> {for(int i = 0; i < 1000; i++){try {blockingQueue.put(i);System.out.println("生产了元素: " + i);} catch (InterruptedException e) {e.printStackTrace();}}});// 消费者线程Thread consumer = new Thread(() -> {while(true){try {// 进行休眠Thread.sleep(1000 * 1);int elem = blockingQueue.take();System.out.println("消费了元素: " + elem);} catch (InterruptedException e) {e.printStackTrace();}}});producer.start();consumer.start();}
}

最后的执行结果如下

在这里插入图片描述
在短时间之内进行大量的生产之后开始隔一秒拿出一个元素, 生产一个元素

阻塞队列的模拟实现

关于wait和while的搭配使用

在这里插入图片描述
上面是我们的JDK帮助文档wait使用的建议(其实就是源码), 我们官方文档中提倡wait的使用建议和while循环搭配, 而不是和if搭配…原因下面解释

模拟实现

其实就是一个循环队列, 在put方法加入元素的时候如果队列是满的就进行阻塞, 在take方法拿出元素的时候如果队列是空的也进行阻塞(使用wait), 然后put方法添加了一个元素之后, 使用notify方法对take正在阻塞的线程进行唤醒(随机唤醒), 下面是实现代码


/*** 自己实现一个阻塞队列* 1. 使用循环数组* 2. 使用wait-notify进行线程见的通信* 3. 关于wait的使用的while机制*/
public class MyBlockingQueue {// 我们定义这个阻塞队列中的元素是int类型private int capacity = 0;private int[] queue = null;// 构造方法public MyBlockingQueue(int capacity) {this.capacity = capacity;queue = new int[capacity];}// 定义队首尾的指针以及元素个数private int first = 0;private int last = 0;private int size = 0;// 判断队列是否为空private boolean isEmpty() {return size == 0;}// 判断队列是否是满的private boolean isFull() {return size == capacity;}// put操作public void put(int val) throws InterruptedException {while (isFull()) {// 此时进入阻塞等待synchronized (this) {this.wait();}}queue[last] = val;last = (last + 1) % capacity;size++;// 随机唤醒一个线程synchronized (this) {this.notify();}}// take操作public int take() throws InterruptedException {while (isEmpty()) {// 此时进入阻塞等待synchronized (this) {this.wait();}}int res = queue[first];first = (first + 1) % capacity;size--;// 随机唤醒一个线程synchronized (this) {this.notify();}return res;}
}class Test {public static void main(String[] args) {// 对实现的队列进行测试MyBlockingQueue myBlockingQueue = new MyBlockingQueue(100);// 创建生产者线程进行测试Thread producer = new Thread(() -> {for(int i = 0; i < 1000; i++){try {myBlockingQueue.put(i);System.out.println("生产了元素: " + i);} catch (InterruptedException e) {e.printStackTrace();}}});// 创建消费者线程进行测试Thread consumer = new Thread(() -> {for(int i = 0; i < 1000; i++){try {Thread.sleep(1000);int getElem = myBlockingQueue.take();System.out.println("消费了元素: " + getElem);} catch (InterruptedException e) {e.printStackTrace();}}});// 启动两个线程producer.start();consumer.start();}
}

在这里插入图片描述
瞬间产出100个元素之后进行阻塞, 产出一个消耗一个…


为什么要使用while代替if

 // put操作public void put(int val) throws InterruptedException {if(isFull()) {// 此时进入阻塞等待synchronized (this) {this.wait();}}queue[last] = val;last = (last + 1) % capacity;size++;// 随机唤醒一个线程synchronized (this) {this.notify();}}// put操作public void put(int val) throws InterruptedException {while (isFull()) {// 此时进入阻塞等待synchronized (this) {this.wait();}}queue[last] = val;last = (last + 1) % capacity;size++;// 随机唤醒一个线程synchronized (this) {this.notify();}}

我们分析一下两个相同的操作, 使用whileif的区别

在这里插入图片描述
这一张图片揭示了为什么使用wait搭配while使用更加合理

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

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

相关文章

qt QCryptographicHash详解

1、概述 QCryptographicHash是Qt框架中提供的一个类&#xff0c;用于实现加密散列函数&#xff0c;即哈希函数。哈希函数能够将任意长度的数据转换为固定长度的哈希值&#xff0c;也称为散列值或数据指纹。这个哈希值通常用于数据的完整性校验、密码存储等场景。QCryptographi…

【数据结构】动态规划-基础篇

针对动态规划问题&#xff0c;我总结了以下5步&#xff1a; 确定dp数组以及下标的含义&#xff1b; 递推公式&#xff1b; dp数组如何初始化&#xff1b; 遍历顺序&#xff1b; 打印dp数组&#xff08;用来debug&#xff09;&#xff1b; 以上5步适用于任何动态规划问题&#x…

字符串函数和内存函数

字符串函数 1、strlcpy 【字符串拷贝】 &#xff08;将原字符串中的字符拷贝到目标字符数组中&#xff0c;包括终止符号\0&#xff0c;并在这里停止&#xff1b;为了避免越界&#xff0c;目标字符串数组应该足够大去接收&#xff09;&#x1f446; &#xff08;返回值是 dest…

Electron-vue 框架升级 Babel7 并支持electron-preload webapck 4 打包过程记录

前言 我这边一直用的electron-vue框架是基于electron 21版本的&#xff0c;electron 29版本追加了很多新功能&#xff0c;但是这些新功能对开发者不友好&#xff0c;对electron构建出来的软件&#xff0c;使用者更安全&#xff0c;所以&#xff0c;我暂时不想研究electron 29版…

ML 系列:第 39 节 - 估计方法:最大似然估计 (MLE)

目录 一、说明 二、什么是最大似然估计 (MLE)&#xff1f; 2.1 理解公式 2.2 MLE 的定义 2.3 我们何时使用 MLE&#xff1f; 三、结论 一、说明 在统计学领域&#xff0c;我们经常需要根据观察到的数据估计统计模型的参数。为此目的广泛使用的两种关键方法是最大似然估计 ( MLE…

文本生成类(机器翻译)系统评估

在机器翻译任务中常用评价指标&#xff1a;BLEU、ROGUE、METEOR、PPL。 这些指标的缺点&#xff1a;只能反应模型输出是否类似于测试文本。 BLUE&#xff08;Bilingual Evaluation Understudy&#xff09;&#xff1a;是用于评估模型生成的句子(candidate)和实际句子(referen…

vue.js学习(day 19)

自定义创建项目 ESlint 代码规范 代码规范错误 手动修正 自动修正 settings.json {"emmet.triggerExpansionOnTab": true,"editor.fontSize": 25,// 当保存的时候&#xff0c;eslint自动帮我们修复错误"editor.codeActionsOnSave": {&qu…

两个畸变矩阵相乘后还是一个2*2的矩阵,有四个畸变元素。1、畸变矩阵吸收了法拉第矩阵。2、畸变矩阵也给法拉第旋转角带来模糊(求解有多种可能)

角度一&#xff1b;恢复畸变的时候也把法拉第旋转恢复了 角度二&#xff1a;求解法拉第旋转角的时候 前面乘的复系数的不同也会带来法拉第旋转角和畸变的不同解 注意&#xff1a;无论多少个畸变矩阵相乘&#xff0c;结果都是2*2的矩阵&#xff0c;也就是畸变参数可以减少…

VR眼镜可视化编程:开启医疗信息系统新纪元

一、引言 随着科技的飞速发展&#xff0c;VR 可视化编程在医疗信息系统中的应用正逐渐成为医疗领域的新趋势。它不仅为医疗教育、手术培训、疼痛管理等方面带来了新的机遇&#xff0c;还在提升患者体验、推动医疗信息系统智能化等方面发挥着重要作用。 在当今医疗领域&#xf…

禾川Q1系列PLC控制X3E总线伺服

1、建立链接 2、配置EtherCAT总线 3、添加Cia402轴 4、添加总线设备 5、总线轴控FB建立 代码部分1

股市复盘笔记

复盘是股市投资中非常重要的一个环节&#xff0c;它指的是投资者在股市收盘后&#xff0c;对当天的市场走势、个股表现以及自己的交易行为进行回顾和总结&#xff0c;以便更好地指导未来的投资决策。以下是对复盘的详细解释&#xff1a; 一、复盘的目的 总结市场走势&#xff…

基于TensorFlow框架的线性回归实现

目录 ​编辑 线性回归简介 TensorFlow简介 线性回归模型的TensorFlow实现 1. 安装TensorFlow 2. 导入必要的库 3. 准备数据 4. 定义模型 5. 定义损失函数 6. 定义优化器 7. 训练模型 8. 评估模型 9. 模型参数的可视化 10. 模型预测的准确性评估 结论 在统计学和…

网页端五子棋对战(四)---玩家匹配实现上线下线处理

文章目录 1.游戏大厅用户匹配1.1请求和响应1.2设计匹配页面1.3获取玩家信息1.4玩家信息的样式设置1.5初始化我们的websocket1.6点击按钮和客户端交互1.7点击按钮和服务器端交互 2.服务器端实现匹配功能框架2.1方法重写2.2借用session 3.处理上线下线3.1什么是上线下线3.2实现用…

「Mac畅玩鸿蒙与硬件42」UI互动应用篇19 - 数字键盘应用

本篇将带你实现一个数字键盘应用&#xff0c;支持用户通过点击数字键输入数字并实时更新显示内容。我们将展示如何使用按钮组件和状态管理来实现一个简洁且实用的数字键盘。 关键词 UI互动应用数字键盘按钮组件状态管理用户交互 一、功能说明 数字键盘应用将实现以下功能&…

cgo内存泄漏排查

示例程序&#xff1a; package main/* #include <stdlib.h> #include <string.h> #include <stdio.h> char* cMalloc() {char *mem (char*)malloc(1024 * 1024 * 16);return mem; } void cMemset(char* mem) {memset(mem, -, 1024 * 1024 * 16); } int arr…

红日靶场vulnstack (五)

前言 好久没打靶机了&#xff0c;今天有空搞了个玩一下&#xff0c;红日5比前面的都简单。 靶机环境 win7&#xff1a;192.168.80.150(外)、192.168.138.136(内) winserver28&#xff08;DC&#xff09;&#xff1a;192.168.138.138 环境搭建就不说了&#xff0c;和之前写…

汽车IVI中控开发入门及进阶(三十七):基于HFP协议的蓝牙电话

概述: HFP全称Hands-free Profile,是一款让蓝牙设备控制电话的软件,多用于汽车上。此类设备最常见的例子是车载免提装置与蜂窝电话或可穿戴无线耳机一起使用。该配置文件定义了支持免提配置文件的两个设备如何在点对点的基础上相互交互。免提模式的实现通常使耳机或嵌入式免…

线程条件变量 生产者消费者模型 Linux环境 C语言实现

只能用来解决同步问题&#xff0c;且不能独立使用&#xff0c;必须配合互斥锁一起用 头文件&#xff1a;#include <pthread.h> 类型&#xff1a;pthread_cond_t PTHREAD_COND_INITIALIZER 初始化 初始化&#xff1a;int pthread_cond_init(pthread_cond_t * cond, NULL);…

AI技术在电商行业中的应用与发展

✨✨ 欢迎大家来访Srlua的博文&#xff08;づ&#xffe3;3&#xffe3;&#xff09;づ╭❤&#xff5e;✨✨ &#x1f31f;&#x1f31f; 欢迎各位亲爱的读者&#xff0c;感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua小谢&#xff0c;在这里我会分享我的知识和经验。&am…

高通---Camera调试流程及常见问题分析

文章目录 一、概述二、Camera配置的整体流程三、Camera的代码架构图四、Camera数据流的传递五、camera debug FAQ 一、概述 在调试camera过程中&#xff0c;经常会遇到各种状况&#xff0c;本篇文章对camera调试的流程进行梳理。对常见问题的提供一些解题思路。 二、Camera配…