JUC并发工具类在大厂的应用场景详解

news2024/11/16 13:35:10
        jdk提供了比synchronized更加高级的各种同步工具,包括 ReentrantLock Semaphore CountDownLatch 、 CyclicBarrier等,可以实现更加丰富的多线程操作。
(前三个是重点)

        一. ReentrantLock

        ReentrantLock是一种可重入的独占锁,它允许同一个线程多次获取同一个锁而不会被阻塞。 它的功能类似于synchronized是一种互斥锁,可以保证线程安全。相对于 synchronized, ReentrantLock具备如下特点:

        1.1 可中断

        1.2 可以设置超时时间

        1.3 可以设置为公平锁(默认非公平锁)
        1.4 支持多个条件变量
        1.5 与 synchronized 一样,都支持可重入
它的主要应用场景是 在多线程环境下对共享资源进行独占式访问,以保证数据的一致性和安全性。
        常用API
void lock()
获取锁,调用该方法当前线程会获取锁,当锁获
得后,该方法返回
void lockInterruptibly()
可中断的获取锁,和lock()方法不同之处在于该方
法会响应中断,即在锁的获取中可以中断当前线
boolean tryLock()
尝试非阻塞的获取锁,调用该方法后立即返回。
如果能够获取到返回true,否则返回false
boolean tryLock(long time, TimeUnit unit)
超时获取锁,当前线程在以下三种情况下会被返
回:
当前线程在超时时间内获取了锁
当前线程在超时时间内被中断
超时时间结束,返回false
void unlock()
释放锁
Condition newCondition()
获取等待通知组件,该组件和当前的锁绑定,当 前线程只有获取了锁,才能调用该组件的await() 方法,而调用后,当前线程将释放锁

注意点:   

        1. 默认情况下 ReentrantLock 为非公平锁而非公平锁;
        2. 加锁次数和释放锁次数一定要保持一致,否则会导致线程阻塞或程序异常;
        3. 加锁操作一定要放在 try 代码之前,这样可以避免未加锁成功又释放锁的异常;
        4. 释放锁一定要放在 finally 中,否则会导致线程阻塞。

        1.1 ReentrantLock使用

        独占锁:模拟抢票场景

package com.laoyang.Thread;

import java.util.concurrent.locks.ReentrantLock;

/**
 * @author:Kevin
 * @create: 2023-10-11 18:17
 * @Description: 模拟抢票
 */

public class ReentrantLockDemo {
    private final ReentrantLock lock = new ReentrantLock();//默认非公平
    private static int tickets = 8; // 总票数

    public void buyTicket() {
        lock.lock(); // 获取锁
        try {
            if (tickets > 0) { // 还有票
                try {
                    Thread.sleep(10); // 休眠10ms,模拟出并发效果
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "购买了第" +
                        tickets-- + "张票");
            } else {
                System.out.println("票已经卖完了," + Thread.currentThread().getName() +
                        "抢票失败");
            }

        } finally {
            lock.unlock(); // 释放锁
        }
    }


    public static void main(String[] args) {
        ReentrantLockDemo ticketSystem = new ReentrantLockDemo();
        for (int i = 1; i <= 10; i++) {
            Thread thread = new Thread(() -> {

                ticketSystem.buyTicket(); // 抢票

            }, "线程" + i);
            // 启动线程
            thread.start();

        }


        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("剩余票数:" + tickets);
    }
}

        

       1.2 公平锁和非公平锁

        ReentrantLock支持公平锁和非公平锁两种模式:

        公平锁:线程在获取锁时,按照等待的先后顺序获取锁。

        非公平锁:线程在获取锁时,不按照等待的先后顺序获取锁,而是随机获取锁。ReentrantLock默认是非公平锁
        比如买票的时候就有可能出现插队的场景,允许插队就是非公平锁,如下图:

        1.3 结合Condition实现生产者消费者模式

java.util.concurrent类库中提供Condition类来实现线程之间的协调。调用Condition.await() 方法使
线程等待,其他线程调用Condition.signal() 或 Condition.signalAll() 方法唤醒等待的线程。
注意: 调用Condition的await()和signal()方法,都必须在lock保护之内。
案例:基于ReentrantLock和Condition实现一个简单队列
package com.laoyang.Thread;


import java.util.Random;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author:Kevin
 * @create: 2023-10-11 18:29
 * @Description: 模拟生产消费场景
 */

public class ReentrantLockDemo3 {

    public static void main(String[] args) {
        // 创建队列
        Queue queue = new Queue(5);
        //启动生产者线程
        new Thread(new Producer(queue)).start();
        //启动消费者线程
        new Thread(new Customer(queue)).start();
    }

    public static class Queue {
        private Object[] items;
        int size = 0;
        int takeIndex;
        int putIndex;
        private ReentrantLock lock;
        public Condition notEmpty; //消费者线程阻塞唤醒条件,队列为空阻塞,生产者生产完唤醒
        public Condition notFull; //生产者线程阻塞唤醒条件,队列满了阻塞,消费者消费完唤醒

        public Queue(int capacity) {
            this.items = new Object[capacity];
            lock = new ReentrantLock();
            notEmpty = lock.newCondition();
            notFull = lock.newCondition();
        }


        public void put(Object value) throws Exception {
            //加锁
            lock.lock();
            try {
                while (size == items.length)
                    // 队列满了让生产者等待
                    notFull.await();

                items[putIndex] = value;
                if (++putIndex == items.length)
                    putIndex = 0;
                size++;
                notEmpty.signal(); // 生产完唤醒消费者

            } finally {
                System.out.println("producer生产:" + value);
                //解锁
                lock.unlock();
            }
        }

        public Object take() throws Exception {
            lock.lock();
            try {
                // 队列空了就让消费者等待
                while (size == 0)
                    notEmpty.await();

                Object value = items[takeIndex];
                items[takeIndex] = null;
                if (++takeIndex == items.length)
                    takeIndex = 0;
                size--;
                notFull.signal(); //消费完唤醒生产者生产
                return value;
            } finally {
                lock.unlock();
            }
        }
    }

    static class Producer implements Runnable {

        private Queue queue;

        public Producer(Queue queue) {
            this.queue = queue;
        }

        @Override
        public void run() {
            try {
                // 隔1秒轮询生产一次
                while (true) {
                    Thread.sleep(1000);
                    queue.put(new Random().nextInt(1000));
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 101 * 消费者
     * 102
     */
    static class Customer implements Runnable {

        private Queue queue;

        public Customer(Queue queue) {
            this.queue = queue;
        }

        @Override
        public void run() {
            try {
                // 隔2秒轮询消费一次
                while (true) {
                    Thread.sleep(2000);
                    System.out.println("consumer消费:" + queue.take());
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }


}
1.3 应用场景总结
        
ReentrantLock具体应用场景如下:
        1. 解决多线程竞争资源的问题,例如多个线程同时对同一个数据库进行写操作,可以使用ReentrantLock保证每次
只有一个线程能够写入。
        2. 实现多线程任务的顺序执行,例如在一个线程执行完某个任务后,再让另一个线程执行任务。
        3. 实现多线程等待/通知机制,例如在某个线程执行完某个任务后,通知其他线程继续执行任务。

        二. Semaphore 

        Semaphore(信号量)是一种用于多线程编程的同步工具,用于控制同时访问某个资源的线程数量

        Semaphore维护了一个计数器,线程可以通过调用acquire()方法来获取Semaphore中的许可证,当计数器为0时,调用acquire()的线程将被阻塞,直到有其他线程释放许可证;线程可以通过调用release()方法来释放Semaphore中的许可证,这会使Semaphore中的计数器增加,从而允许更多的线程访问共享资源。

2.1 常用方法

acquire()表示阻塞并获取许可
tryAcquire()方法在没有许可的情况下会立即返回 false,要获取许可的线程不会阻塞
release()表示释放许可

2.2 Semaphore实现服务接口限流

package com.laoyang.Thread;

import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;

/**
 * @author:Kevin
 * @create: 2023-10-11 18:48
 * @Description: 模拟限流操作
 */

public class SemaphoreDemo {

    //信号量
    private static Semaphore semaphore = new Semaphore(2);

    private static Executor executor = Executors.newFixedThreadPool(10);


    public static void main(String[] args) {
        for(int i=0;i<10;i++){
             executor.execute(()->getProductInfo2());
        }
    }

    public static String getProductInfo2() {

         if(!semaphore.tryAcquire()){
             System.out.println("请求被流控了");
             return "请求被流控了";
             }
         try {
             System.out.println("请求服务");
             Thread.sleep(2000);
             } catch (InterruptedException e) {
             throw new RuntimeException(e);
             }finally {
             semaphore.release();
             }
         return "返回商品详情信息";
         }
}

2.3 应用场景总结

        以下是一些使用Semaphore的常见场景:
        1. 限流:Semaphore可以用于限制对共享资源的并发访问数量,以控制系统的流量。
        2. 资源池:Semaphore可以用于实现资源池,以维护一组有限的共享资源。

        

        三. CountDownLatch 

        CountDownLatch(闭锁)是一个同步协助类,允许一个或多个线程等待,直到其他线程完成操作集。

        CountDownLatch使用给定的计数值(count)初始化。await方法会阻塞直到当前的计数值 (count),由于countDown方法的调用达到0,count为0之后所有等待的线程都会被释放,并且随后对await方法的调用都会立即返回。这是一个一次性现象 —— count不会被重置。

        3.1 常用方法

void await ()
调用 await() 方法的线程会被挂起,它会等待直到 count 值为 0 才继续执行
boolean await ( long timeout , TimeUnit unit )
await() 类似,若等待 timeout 时长后, count 值还是没有变为 0 ,不再等待,继续执行
void countDown ()
会将 count 1 ,直至为 0

        3.2 CountDownLatch使用

        模拟实现百米赛跑

package com.laoyang.Thread;

import java.util.concurrent.CountDownLatch;

/**
 * @author:Kevin
 * @create: 2023-10-11 18:59
 * @Description: 模拟实现百米赛跑
 */

public class CountDownLatchDemo {

    // begin 代表裁判 初始为 1
    private static CountDownLatch begin = new CountDownLatch(1);

    // end 代表玩家 初始为 8
    private static CountDownLatch end = new CountDownLatch(8);


    public static void main(String[] args) throws InterruptedException {
        for (int i = 1; i <= 8; i++) {
             new Thread(new Runnable() {
                @Override
                public void run() {
                    // 预备状态
                    System.out.println("参赛者" + Thread.currentThread().getName() + "已经准备好了");
                    // 等待裁判吹哨
                    try {
                        begin.await();
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    // 开始跑步
                    System.out.println("参赛者" + Thread.currentThread().getName() + "开始跑步");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    // 跑步结束, 跑完了
                    System.out.println("参赛者" + Thread.currentThread().getName() + "到达终点");
                            // 跑到终点, 计数器就减一
                            end.countDown();
                }
            }).start();
        }
        // 等待 5s 就开始吹哨
        Thread.sleep(5000);
        System.out.println("开始比赛");
        // 裁判吹哨, 计数器减一
        begin.countDown();
        // 等待所有玩家到达终点
        end.await();
        System.out.println("比赛结束");
    }
}

        多任务完成后合并汇总

        很多时候,我们的并发任务,存在前后依赖关系;比如数据详情页需要同时调用多个接口获取数据,并发请求获取到数据后、需要进行结果合并;或者多个数据操作完成后,需要数据check。

package com.laoyang.Thread;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ThreadLocalRandom;

/**
 * @author:Kevin
 * @create: 2023-10-11 19:10
 * @Description: 多任务完成后合并汇总
 */

public class CountDownLatchDemo2 {

    public static void main(String[] args) throws Exception {
        CountDownLatch countDownLatch = new CountDownLatch(5);
        for (int i = 0; i < 5; i++) {
            final int index = i;
            new Thread(() -> {
                try {
                    Thread.sleep(1000 + ThreadLocalRandom.current().nextInt(2000));
                    System.out.println("任务" + index + "执行完成");
                    countDownLatch.countDown();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();
        }

        // 主线程在阻塞,当计数器为0,就唤醒主线程往下执行
        countDownLatch.await();
        System.out.println("主线程:在所有任务运行完成后,进行结果汇总");

    }
}

3.3 应用场景总结

以下是使用CountDownLatch的常见场景:
         1. 并行任务同步:CountDownLatch可以用于协调多个并行任务的完成情况,确保所有任务都完成后再继续执行下
一步操作。
        2. 多任务汇总:CountDownLatch可以用于统计多个线程的完成情况,以确定所有线程都已完成工作。
        3. 资源初始化:CountDownLatch可以用于等待资源的初始化完成,以便在资源初始化完成后开始使用。

          四. CyclicBarrier

        CyclicBarrier(回环栅栏或循环屏障),是 Java 并发库中的一个同步工具,通过它可以实现让一组线程等待至某个状态(屏障点)之后再全部同时执行。叫做回环是因为当所有等待线程都被释放以后,CyclicBarrier可以被重用。

        4.1 常用方法

int await ()
指定数量的线程全部调用 await() 方法时,这些线程不再阻塞
int await ( long timeout , TimeUnit unit )
BrokenBarrierException 表示栅栏已经被破坏,破坏的原因可能是其中一个线程 await() 时被中断
或者超时
void reset ()
循环 通过 reset() 方法可以进行重置

        4.2 CyclicBarrier使用

        利用CyclicBarrier的计数器能够重置,屏障可以重复使用的特性,可以支持类似“人满发车”的场景

package com.laoyang.Thread;

import java.util.concurrent.*;

/**
 * @author:Kevin
 * @create: 2023-10-11 19:20
 * @Description:模拟人满发车
 */

public class CyclicBarrierDemo {
    public static void main(String[] args) {

        ExecutorService executorService = Executors.newFixedThreadPool(5);
        
        CyclicBarrier cyclicBarrier = new CyclicBarrier(5,
                () -> System.out.println("人齐了,准备发车"));

        for (int i = 0; i < 10; i++) {
            final int id = i + 1;
            executorService.submit(new Runnable() {
                @Override
                public void run() {
                    try {
                        System.out.println(id + "号马上就到");
                        int sleepMills = ThreadLocalRandom.current().nextInt(2000);
                        Thread.sleep(sleepMills);
                        System.out.println(id + "号到了,上车");
                        cyclicBarrier.await();

                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } catch (BrokenBarrierException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    }
}

4.3 应用场景总结

以下是一些常见的 CyclicBarrier 应用场景:
        1. 多线程任务:CyclicBarrier 可以用于将复杂的任务分配给多个线程执行,并在所有线程完成工作后触发后续操
作。
        2. 数据处理:CyclicBarrier 可以用于协调多个线程间的数据处理,在所有线程处理完数据后触发后续操作。

4.4 CyclicBarrier 与 CountDownLatch 区别

        1. CountDownLatch 是一次性的,CyclicBarrier 是可循环利用的
        2. CountDownLatch 参与的线程的职责是不一样的,有的在倒计时,有的在等待倒计时结束。CyclicBarrier 参与的线程职责是一样的。

        五. Exchanger 

        Exchanger是一个用于线程间协作的工具类,用于两个线程间交换数据。具体交换数据是通过exchange方法来实现的,如果一个线程先执行exchange方法,那么它会同步等待另一个线程也执行 exchange方法,这个时候两个线程就都达到了同步点,两个线程就可以交换数据。

        5.1 Exchanger使用

        模拟交易场景

用一个简单的例子来看下Exchanger的具体使用。两方做交易,如果一方先到要等另一方也到了才能交易,交易就是执行exchange方法交换数据。
package com.laoyang.Thread;

import java.util.concurrent.Exchanger;

/**
 * @author:Kevin
 * @create: 2023-10-11 19:25
 * @Description: 模拟交换
 */

public class ExchangerDemo {
    private static Exchanger exchanger = new Exchanger();
    static String goods = "电脑";
    static String money = "$4000";

    public static void main(String[] args) throws InterruptedException {
        System.out.println("准备交易,一手交钱一手交货...");
        // 卖家
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("卖家到了,已经准备好货:" + goods);
                try {
                    String money = (String) exchanger.exchange(goods);
                    System.out.println("卖家收到钱:" + money);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();

        Thread.sleep(3000);

        // 买家
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    System.out.println("买家到了,已经准备好钱:" + money);
                    String goods = (String) exchanger.exchange(money);
                    System.out.println("买家收到货:" + goods);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();

    }
}

        5.2 应用场景总结

        Exchanger 可以用于各种应用场景,具体取决于具体的 Exchanger 实现。常见的场景包括:

        1. 数据交换:在多线程环境中,两个线程可以通过 Exchanger 进行数据交换。

        2. 数据采集:在数据采集系统中,可以使用 Exchanger 在采集线程和处理线程间进行数据交换。

        六. Phaser  

        Phaser(阶段协同器)是一个Java实现的并发工具类,用于协调多个线程的执行。它提供了一些方便的方法来管理多个阶段的执行,可以让程序员灵活地控制线程的执行顺序和阶段性的执行。Phaser可以被视为CyclicBarrier和CountDownLatch的进化版,它能够自适应地调整并发线程数,可以动态地增加或减少参与线程的数量。所以Phaser特别适合使用在重复执行或者重用的情况。

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

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

相关文章

spark集群环境下,实现人口平均年龄计算

文章目录 任务目标0. 版本信息1. 计算生成renkou.txt2. 文件上传至spark3. 上传文件时&#xff0c;可能出现的常见错误4. 编写spark文件5. 上传集群6. 集群环境下提交任务 任务目标 在虚拟机上部署spark集群&#xff0c;给定renkou.txt文件&#xff0c;输出平均年龄 renkou.t…

淘宝天猫商品评论数据接口,淘宝天猫商品评论API接口,淘宝API

淘宝商品评论数据接口可以通过淘宝开放平台API获取。 通过构建合理的请求URL&#xff0c;可以向淘宝服务器发起HTTP请求&#xff0c;获取商品评论数据。接口返回的数据一般为JSON格式&#xff0c;包含了商品的各种评价信息。获取到商品评论数据后&#xff0c;可以对其进行处理…

京东商品评论数据接口,京东API接口

京东商品评论内容数据接口步骤如下&#xff1a; 访问京东开放平台并注册一个开发者账号。创建一个应用并获取到API的权限。在开发者的控制台中找到API的使用文档。在文档中找到获取商品评论的API接口&#xff0c;点击获取key和secret。构造请求URL&#xff0c;请求URL的路径为…

SAP-MM/QM 移动原因维护

业务场景&#xff1a; 质检反馈现有的几种退货原因不能满足业务需求&#xff0c;需要增加&#xff0c;那么启用质检的退货原因和未启用质检的退货原因分别在哪里维护呢&#xff1f; 经过查找&#xff0c;退货原因不是按模块区分的&#xff0c;而是按移动类型确定的&#xff0c…

CRM系统:客户培育提高业绩的方法

多数情况下客户线索不会在首次沟通后就表现出强烈购买的意愿&#xff0c;这期间需要经过不断地沟通和培育才能进入到产品购买阶段。CRM客户管理系统帮助销售挖掘价值客户、高效跟进客户直至成交。下面说说&#xff0c;CRM系统如何客户培育提高业绩。 一、筛选潜在客户 企业客…

xcode打包macos报错:FlutterInputs.xcfilelist 和 FlutterOutputs.xcfilelist

xcode 打包macos的时候&#xff0c;报错如下&#xff1a; Unable to load contents of the file list: ‘macos/ephemeral/FlutterInputs.xcfilelist’ ‘macos/ephemeral/FlutterOutputs.xcfilelist’ 解决方案&#xff1a; 我的项目macos下没有找到FlutterInputs.xcfilelis…

[计算机入门] 应用软件介绍(娱乐类)

3.21 应用软件介绍(娱乐类) 3.21.1 音乐&#xff1a;酷狗 音乐软件是一类可以帮助人们播放、管理和发现音乐的应用程序。它们提供了丰富的音乐内容&#xff0c;用户可以通过搜索、分类浏览或个性化推荐等方式找到自己喜欢的歌曲、专辑或艺术家。音乐软件还通常支持创建和管理…

【LeetCode高频SQL50题-基础版】打卡第4天:第21~25题

文章目录 【LeetCode高频SQL50题-基础版】打卡第四天&#xff1a;第21~25题⛅前言即时食物配送II&#x1f512;题目&#x1f511;题解 游戏玩法分析IV&#x1f512;题目&#x1f511;题解 每位教师所教授的科目种类的数量&#x1f512;题目&#x1f511;题解 查询近30天活跃用户…

Adobe发布Firefly 2,提升图像质量和用户体验

&#x1f989; AI新闻 &#x1f680; Adobe发布Firefly 2&#xff0c;提升图像质量和用户体验 摘要&#xff1a;Adobe升级了其AIGC生图平台Firefly为Firefly 2&#xff0c;该版本通过引入矢量图生成功能、提升图像质量和增加多项新功能&#xff0c;大幅改善了用户体验。Firef…

用3-8译码器实现全减器

描述 请使用3-8译码器和必要的逻辑门实现全减器&#xff0c;全减器接口图如下&#xff0c;A是被减数&#xff0c;B是减数&#xff0c;Ci是来自低位的借位&#xff0c;D是差&#xff0c;Co是向高位的借位。 3-8译码器代码如下&#xff0c;可将参考代码添加并例化到本题答案中。 …

Spring 1.依赖 2.xml 3.对象 4.测试

配置模型 依赖注入 很简单 框架很简单 依赖 xml 对象 测试 依赖 写在pom mvn网址找spring 2.写类 就是对象 测试 我们获取对象&#xff08;spring创建&#xff09; 才能用 配置文件 官网找 下拉 粘贴标红 点击它告诉系统是一个 然后第二次就点加入不是新创建

虹科干货 | 虹科带你了解车载以太网-SOME/IP协议

在标准的网络七层架构中&#xff0c;SOME/IP&#xff08;Scalable service-Oriented Middleware over IP) 作为应用层协议运行于车载以太网四层以上&#xff0c;作为以太网通信中间件来实现应用层和IP层的数据交互&#xff0c;使其不依赖于操作系统&#xff0c;又能兼容AUTOSAR…

如何通过Express和React处理SSE

本文作者为360奇舞团前端开发工程师 最近AIGC技术的大热&#xff0c;市面上也出现了许多类似生产的AI工具&#xff0c;其中有一大特色就是对话的输出结果是类似真人的打字效果出现&#xff0c;要呈现出这种效果&#xff0c;最主要的就是要利用SSE技术&#xff08;Server-Sent E…

【新书推荐】AI时代,当程序员遇到ChatGPT,开发效率飞起来!

文章目录 ChatGPT为开发提速一、ChatGPT自动生成代码二、优化代码结构三、自动化测试四、智能推荐五、ChatGPT在开发中的实际应用六、总结 新书推荐《AI时代程序员开发之道&#xff1a;ChatGPT让程序员插上翅膀》内容简介作家简介目录获取方式 ChatGPT为开发提速 人工智能是当…

STM32物联网基于ZigBee智能家居控制系统

实践制作DIY- GC0169-ZigBee智能家居 一、功能说明&#xff1a; 基于STM32单片机设计-ZigBee智能家居 二、功能介绍&#xff1a; 1个主机显示板&#xff1a;STM32F103C最小系统ZigBee无线模块OLED显示器 语音识别模块多个按键ESP8266-WIFI模块&#xff08;仅WIFI版本有&…

c++数据处理----图像修补:cv::inpaint()

图像修补:cv::inpaint() cv::inpaint() 是OpenCV中的一个函数&#xff0c;用于图像修补&#xff08;image inpainting&#xff09;。图像修补是一种图像处理技术&#xff0c;用于去除图像中的损坏或不需要的区域&#xff0c;然后用周围的信息填充这些区域&#xff0c;使图像看…

苹果安卓网页的H5封装成App的应用和原生开发的应用有什么不一样?

老哥在么&#xff1f;H5封装的app和原生开发的app有什么不一样&#xff1f;&#xff0c;不懂就要问&#xff0c;我能理解哈&#xff0c;虽然这个问题有点小白&#xff0c;但是我还是得认真回答&#xff0c;以防止我回答的不是很好&#xff0c;所以我科技了一下&#xff0c;所以…

1688商品评论数据接口,1688商品评论API接口

1688商品评论内容数据接口步骤如下&#xff1a; 通过1688开放平台接口获取商品评论内容数据&#xff1a;首先&#xff0c;您需要在开放平台注册成为开发者并创建一个应用&#xff0c;获取到所需的App Key和App Secret等信息。使用获取到的App Key和App Secret&#xff0c;进行…

智能防眩目前照灯系统控制器ADB

经纬恒润的自适应远光系统—— ADB&#xff08;Adaptive Driving Beam&#xff09; 是一种能够根据路况自适应变换远光光型的智能远光控制系统。根据本车行驶状态、环境状态以及道路车辆状态&#xff0c;ADB 系统自动为驾驶员开启或退出远光。同时&#xff0c;根据车辆前方视野…

深入了解进程:计算机中的任务管理与隔离

什么是进程&#xff1f; 进程是一个独立的执行环境&#xff0c;包括自己的内存空间、程序计数器、文件句柄等。每个进程都是操作系统的一个独立实例&#xff0c;它们之间通常相互隔离。 通俗来说&#xff0c;进程就是程序的一次执行过程&#xff0c;程序是静态的&#xff0c;它…