Java面试知识点(全)- Java并发-多线程JUC二-原子类/锁

news2024/11/16 23:40:21

Java面试知识点(全)
导航: https://nanxiang.blog.csdn.net/article/details/130640392
注:随时更新

JUC原子类

什么是CAS

CAS的全称为Compare-And-Swap,直译就是对比交换。是一条CPU的原子指令,其作用是让CPU先进行比较两个值是否相等,然后原子地更新某个位置的值,经过调查发现,其实现方式是基于硬件平台的汇编指令,就是说CAS是靠硬件实现的,JVM只是封装了汇编调用,那些AtomicInteger类便是使用了这些封装后的接口。   简单解释:CAS操作需要输入两个数值,一个旧值(期望操作前的值)和一个新值,在操作期间先比较下在旧值有没有发生变化,如果没有发生变化,才交换成新值,发生了变化则不交换。
CAS操作是原子性的,所以多线程并发使用CAS更新数据时,可以不使用锁。JDK中大量使用了CAS来更新数据而防止加锁(synchronized 重量级锁)来保持原子更新。
相信sql大家都熟悉,类似sql中的条件更新一样:update set id=3 from table where id=2。因为单条sql执行具有原子性,如果有多个线程同时执行此sql语句,只有一条能更新成功。

CAS使用示例,结合AtomicInteger给出示例

如果不使用CAS,在高并发下,多线程同时修改一个变量的值我们需要synchronized加锁(可能有人说可以用Lock加锁,Lock底层的AQS也是基于CAS进行获取锁的)。

public class Test {
    private int i=0;
    public synchronized int add(){
        return i++;
    }
}
java中为我们提供了AtomicInteger 原子类(底层基于CAS进行更新数据的),不需要加锁就在多线程并发场景下实现数据的一致性。
public class Test {
    private  AtomicInteger i = new AtomicInteger(0);
    public int add(){
        return i.addAndGet(1);
    }
}

CAS会有哪些问题

CAS 方式为乐观锁,synchronized 为悲观锁。因此使用 CAS 解决并发问题通常情况下性能更优。
但使用 CAS 方式也会有几个问题:
- ABA问题
因为CAS需要在操作值的时候,检查值有没有发生变化,比如没有发生变化则更新,但是如果一个值原来是A,变成了B,又变成了A,那么使用CAS进行检查时则会发现它的值没有发生变化,但是实际上却变化了。
ABA问题的解决思路就是使用版本号。在变量前面追加上版本号,每次变量更新的时候把版本号加1,那么A->B->A就会变成1A->2B->3A。
从Java 1.5开始,JDK的Atomic包里提供了一个类AtomicStampedReference来解决ABA问题。这个类的compareAndSet方法的作用是首先检查当前引用是否等于预期引用,并且检查当前标志是否等于预期标志,如果全部相等,则以原子方式将该引用和该标志的值设置为给定的更新值。
- 循环时间长开销大
自旋CAS如果长时间不成功,会给CPU带来非常大的执行开销。如果JVM能支持处理器提供的pause指令,那么效率会有一定的提升。pause指令有两个作用:第一,它可以延迟流水线执行命令(de-pipeline),使CPU不会消耗过多的执行资源,延迟的时间取决于具体实现的版本,在一些处理器上延迟时间是零;第二,它可以避免在退出循环的时候因内存顺序冲突(Memory Order Violation)而引起CPU流水线被清空(CPU Pipeline Flush),从而提高CPU的执行效率。
- 只能保证一个共享变量的原子操作
当对一个共享变量执行操作时,我们可以使用循环CAS的方式来保证原子操作,但是对多个共享变量操作时,循环CAS就无法保证操作的原子性,这个时候就可以用锁。
还有一个取巧的办法,就是把多个共享变量合并成一个共享变量来操作。比如,有两个共享变量i = 2,j = a,合并一下ij = 2a,然后用CAS来操作ij。
从Java 1.5开始,JDK提供了AtomicReference类来保证引用对象之间的原子性,就可以把多个变量放在一个对象里来进行CAS操作。

AtomicInteger底层实现

- CAS+volatile ,cas保证线程的原子性
- volatile保证线程的可见性,多线程并发时,一个线程修改数据,可以保证其它线程立马看到修改后的值CAS 保证数据更新的原子性。

Java原子类的理解

包含13个,4组分类,说说作用和使用场景。
• 原子更新基本类型
○ AtomicBoolean: 原子更新布尔类型。
○ AtomicInteger: 原子更新整型。
○ AtomicLong: 原子更新长整型。
• 原子更新数组
○ AtomicIntegerArray: 原子更新整型数组里的元素。
○ AtomicLongArray: 原子更新长整型数组里的元素。
○ AtomicReferenceArray: 原子更新引用类型数组里的元素。
• 原子更新引用类型
○ AtomicIntegerFieldUpdater: 原子更新整型的字段的更新器。
○ AtomicLongFieldUpdater: 原子更新长整型字段的更新器。
○ AtomicStampedFieldUpdater: 原子更新带有版本号的引用类型。
○ AtomicReferenceFieldUpdater: 上面已经说过此处不在赘述
• 原子更新字段类
○ AtomicReference: 原子更新引用类型。
○ AtomicStampedReference: 原子更新引用类型, 内部使用Pair来存储元素值及其版本号。
○ AtomicMarkableReferce: 原子更新带有标记位的引用类型。

AtomicStampedReference是怎么解决ABA的?

AtomicStampedReference主要维护包含一个对象引用以及一个可以自动更新的整数"stamp"的pair对象来解决ABA问题。

JUC锁

为什么LockSupport也是核心基础类

AQS框架借助于两个类:Unsafe(提供CAS操作)和LockSupport(提供park/unpark操作)

通过wait/notify实现同步

class MyThread extends Thread {
    
    public void run() {
        synchronized (this) {
            System.out.println("before notify");            
            notify();
            System.out.println("after notify");    
        }
    }
}
public class WaitAndNotifyDemo {
    public static void main(String[] args) throws InterruptedException {
        MyThread myThread = new MyThread();            
        synchronized (myThread) {
            try {        
                myThread.start();
                // 主线程睡眠3s
                Thread.sleep(3000);
                System.out.println("before wait");
                // 阻塞主线程
                myThread.wait();
                System.out.println("after wait");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }            
        }        
    }
}
运行结果
before wait
before notify
after notify
after wait

通过LockSupport的park/unpark实现同步

import java.util.concurrent.locks.LockSupport;
class MyThread extends Thread {
    private Object object;
public MyThread(Object object) {
        this.object = object;
    }
public void run() {
        System.out.println("before unpark");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 获取blocker
        System.out.println("Blocker info " + LockSupport.getBlocker((Thread) object));
        // 释放许可
        LockSupport.unpark((Thread) object);
        // 休眠500ms,保证先执行park中的setBlocker(t, null);
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 再次获取blocker
        System.out.println("Blocker info " + LockSupport.getBlocker((Thread) object));
System.out.println("after unpark");
    }
}
public class test {
    public static void main(String[] args) {
        MyThread myThread = new MyThread(Thread.currentThread());
        myThread.start();
        System.out.println("before park");
        // 获取许可
        LockSupport.park("ParkAndUnparkDemo");
        System.out.println("after park");
    }
}

运行结果:
before park
before unpark
Blocker info ParkAndUnparkDemo
after park
Blocker info null
after unpark
说明: 本程序先执行park,然后在执行unpark,进行同步,并且在unpark的前后都调用了getBlocker,可以看到两次的结果不一样,并且第二次调用的结果为null,这是因为在调用unpark之后,执行了Lock.park(Object blocker)函数中的setBlocker(t, null)函数,所以第二次调用getBlocker时为null。
上例是先调用park,然后调用unpark,现在修改程序,先调用unpark,然后调用park,看能不能正确同步。具体代码如下

import java.util.concurrent.locks.LockSupport;
class MyThread extends Thread {
    private Object object;
public MyThread(Object object) {
        this.object = object;
    }
public void run() {
        System.out.println("before unpark");        
        // 释放许可
        LockSupport.unpark((Thread) object);
        System.out.println("after unpark");
    }
}
public class ParkAndUnparkDemo {
    public static void main(String[] args) {
        MyThread myThread = new MyThread(Thread.currentThread());
        myThread.start();
        try {
            // 主线程睡眠3s
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("before park");
        // 获取许可
        LockSupport.park("ParkAndUnparkDemo");
        System.out.println("after park");
    }
}

运行结果:
before unpark
after unpark
before park
after park
说明: 可以看到,在先调用unpark,再调用park时,仍能够正确实现同步,不会造成由wait/notify调用顺序不当所引起的阻塞。因此park/unpark相比wait/notify更加的灵活。

Thread.sleep()、Object.wait()、Condition.await()、LockSupport.park()的区别

• Thread.sleep()和Object.wait()的区别

首先,我们先来看看Thread.sleep()和Object.wait()的区别,这是一个烂大街的题目了,大家应该都能说上来两点。
1. Thread.sleep()不会释放占有的锁,Object.wait()会释放占有的锁;
2. Thread.sleep()必须传入时间,Object.wait()可传可不传,不传表示一直阻塞下去;
3. Thread.sleep()到时间了会自动唤醒,然后继续执行;
4. Object.wait()不带时间的,需要另一个线程使用Object.notify()唤醒;
5. Object.wait()带时间的,假如没有被notify,到时间了会自动唤醒,这时又分好两种情况,一是立即获取到了锁,线程自然会继续执行;二是没有立即获取锁,线程进入同步队列等待获取锁;
其实,他们俩最大的区别就是Thread.sleep()不会释放锁资源,Object.wait()会释放锁资源。
• Object.wait()和Condition.await()的区别
Object.wait()和Condition.await()的原理是基本一致的,不同的是Condition.await()底层是调用LockSupport.park()来实现阻塞当前线程的。
实际上,它在阻塞当前线程之前还干了两件事,一是把当前线程添加到条件队列中,二是“完全”释放锁,也就是让state状态变量变为0,然后才是调用LockSupport.park()阻塞当前线程。
• Thread.sleep()和LockSupport.park()的区别 LockSupport.park()还有几个兄弟方法——parkNanos()、parkUtil()等,我们这里说的park()方法统称这一类方法。
1. 从功能上来说,Thread.sleep()和LockSupport.park()方法类似,都是阻塞当前线程的执行,且都不会释放当前线程占有的锁资源;
2. Thread.sleep()没法从外部唤醒,只能自己醒过来;
3. LockSupport.park()方法可以被另一个线程调用LockSupport.unpark()方法唤醒;
4. Thread.sleep()方法声明上抛出了InterruptedException中断异常,所以调用者需要捕获这个异常或者再抛出;
5. LockSupport.park()方法不需要捕获中断异常;
6. Thread.sleep()本身就是一个native方法;
7. LockSupport.park()底层是调用的Unsafe的native方法;
• Object.wait()和LockSupport.park()的区别
二者都会阻塞当前线程的运行,他们有什么区别呢? 经过上面的分析相信你一定很清楚了,真的吗? 往下看!
1. Object.wait()方法需要在synchronized块中执行;
2. LockSupport.park()可以在任意地方执行;
3. Object.wait()方法声明抛出了中断异常,调用者需要捕获或者再抛出;
4. LockSupport.park()不需要捕获中断异常;
5. Object.wait()不带超时的,需要另一个线程执行notify()来唤醒,但不一定继续执行后续内容;
6. LockSupport.park()不带超时的,需要另一个线程执行unpark()来唤醒,一定会继续执行后续内容;
park()/unpark()底层的原理是“二元信号量”,你可以把它相像成只有一个许可证的Semaphore,只不过这个信号量在重复执行unpark()的时候也不会再增加许可证,最多只有一个许可证。

如果在wait()之前执行了notify()会怎样

如果当前的线程不是此对象锁的所有者,却调用该对象的notify()或wait()方法时抛出IllegalMonitorStateException异常;
如果当前线程是此对象锁的所有者,wait()将一直阻塞,因为后续将没有其它notify()唤醒它。

如果在park()之前执行了unpark()会怎样

线程不会被阻塞,直接跳过park(),继续执行后续内容

什么是AQS? 为什么它是核心?

AQS是一个用来构建锁和同步器的框架,使用AQS能简单且高效地构造出应用广泛的大量的同步器,比如我们提到的ReentrantLock,Semaphore,其他的诸如ReentrantReadWriteLock,SynchronousQueue,FutureTask等等皆是基于AQS的。
AQS核心思想是,如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并且将共享资源设置为锁定状态。如果被请求的共享资源被占用,那么就需要一套线程阻塞等待以及被唤醒时锁分配的机制,这个机制AQS是用CLH队列锁实现的,即将暂时获取不到锁的线程加入到队列中。

AbstractQueuedSynchronizer类底层的数据结构是使用CLH(Craig,Landin,and Hagersten)队列是一个虚拟的双向队列(虚拟的双向队列即不存在队列实例,仅存在结点之间的关联关系)。AQS是将每条请求共享资源的线程封装成一个CLH锁队列的一个结点(Node)来实现锁的分配。其中Sync queue,即同步队列,是双向链表,包括head结点和tail结点,head结点主要用作后续的调度。而Condition queue不是必须的,其是一个单向链表,只有当使用Condition时,才会存在此单向链表。并且可能会有多个Condition queue。

在这里插入图片描述

AQS的核心思想

底层数据结构: AQS核心思想是,如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并且将共享资源设置为锁定状态。如果被请求的共享资源被占用,那么就需要一套线程阻塞等待以及被唤醒时锁分配的机制,这个机制AQS是用CLH队列锁实现的,即将暂时获取不到锁的线程加入到队列中。

AQS有哪些核心的方法

isHeldExclusively()//该线程是否正在独占资源。只有用到condition才需要去实现它。
tryAcquire(int)//独占方式。尝试获取资源,成功则返回true,失败则返回false。
tryRelease(int)//独占方式。尝试释放资源,成功则返回true,失败则返回false。
tryAcquireShared(int)//共享方式。尝试获取资源。负数表示失败;0表示成功,但没有剩余可用资源;正数表示成功,且有剩余资源。
tryReleaseShared(int)//共享方式。尝试释放资源,成功则返回true,失败则返回false。

AQS定义什么样的资源获取方式

AQS定义了两种资源获取方式:
• 独占(只有一个线程能访问执行,又根据是否按队列的顺序分为公平锁和非公平锁,如ReentrantLock)
• 共享(多个线程可同时访问执行,如Semaphore、CountDownLatch、 CyclicBarrier )。ReentrantReadWriteLock可以看成是组合式,允许多个线程同时对某一资源进行读。

AQS底层使用了什么样的设计模式

模板, 共享锁和独占锁在一个接口类中。

Lock

什么是可重入,什么是可重入锁, 它用来解决什么问题

可重入:(来源于维基百科)若一个程序或子程序可以“在任意时刻被中断然后操作系统调度执行另外一段代码,这段代码又调用了该子程序不会出错”,则称其为可重入(reentrant或re-entrant)的。即当该子程序正在运行时,执行线程可以再次进入并执行它,仍然获得符合设计时预期的结果。与多线程并发执行的线程安全不同,可重入强调对单个线程执行时重新进入同一个子程序仍然是安全的。
可重入锁:又名递归锁,是指在同一个线程在外层方法获取锁的时候,再进入该线程的内层方法会自动获取锁(前提锁对象得是同一个对象或者class),不会因为之前已经获取过还没释放而阻塞。

ReentrantLock的核心是AQS,那么它怎么来实现的

ReentrantLock总共有三个内部类,并且三个内部类是紧密相关的,下面先看三个类的关系。

在这里插入图片描述

说明: ReentrantLock类内部总共存在Sync、NonfairSync、FairSync三个类,NonfairSync与FairSync类继承自Sync类,Sync类继承自AbstractQueuedSynchronizer抽象类。下面逐个进行分析。

ReentrantLock是如何实现公平锁的

FairSync

ReentrantLock是如何实现非公平锁的

UnFairSync

ReentrantLock默认实现的是公平还是非公平锁

非公平锁

为了有了ReentrantLock还需要ReentrantReadWriteLock

读锁和写锁分离:ReentrantReadWriteLock表示可重入读写锁,ReentrantReadWriteLock中包含了两种锁,读锁ReadLock和写锁WriteLock,可以通过这两种锁实现线程间的同步。

ReentrantReadWriteLock底层实现原理

ReentrantReadWriteLock有五个内部类,五个内部类之间也是相互关联的。内部类的关系如下图所示。

在这里插入图片描述

说明: 如上图所示,Sync继承自AQS、NonfairSync继承自Sync类、FairSync继承自Sync类;ReadLock实现了Lock接口、WriteLock也实现了Lock接口。

ReentrantReadWriteLock底层读写状态如何设计的

高16位为读锁,低16位为写锁

读锁和写锁的最大数量是多少

2的16次方-1

本地线程计数器ThreadLocalHoldCounter是用来做什么的

本地线程计数器,与对象绑定(线程-》线程重入的次数)

写锁的获取与释放是怎么实现的

tryAcquire/tryRelease

读锁的获取与释放是怎么实现的

tryAcquireShared/tryReleaseShared

什么是锁的升降级

RentrantReadWriteLock为什么不支持锁升级? RentrantReadWriteLock不支持锁升级(把持读锁、获取写锁,最后释放读锁的过程)。目的也是保证数据可见性,如果读锁已被多个线程获取,其中任意线程成功获取了写锁并更新了数据,则其更新对其他获取到读锁的线程是不可见的。

ReentrantReadWriteLock样例


/**
 * @author weidong
 * @version V1.0.0
 * @description
 * @since 2023/5/19
 */
class ReadThread extends Thread {
    private ReentrantReadWriteLock rrwLock;

    public ReadThread(String name, ReentrantReadWriteLock rrwLock) {
        super(name);
        this.rrwLock = rrwLock;
    }

    public void run() {
        System.out.println(Thread.currentThread().getName() + " trying to lock");
        try {
            rrwLock.readLock().lock();
            System.out.println(Thread.currentThread().getName() + " lock successfully");
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            rrwLock.readLock().unlock();
            System.out.println(Thread.currentThread().getName() + " unlock successfully");
        }
    }
}

class WriteThread extends Thread {
    private ReentrantReadWriteLock rrwLock;

    public WriteThread(String name, ReentrantReadWriteLock rrwLock) {
        super(name);
        this.rrwLock = rrwLock;
    }

    public void run() {
        System.out.println(Thread.currentThread().getName() + " trying to lock");
        try {
            rrwLock.writeLock().lock();
            System.out.println(Thread.currentThread().getName() + " lock successfully");
        } finally {
            rrwLock.writeLock().unlock();
            System.out.println(Thread.currentThread().getName() + " unlock successfully");
        }
    }
}

public class ReentrantReadWriteLockDemo {
    public static void main(String[] args) {
        ReentrantReadWriteLock rrwLock = new ReentrantReadWriteLock();
        ReadThread rt1 = new ReadThread("rt1", rrwLock);
        ReadThread rt2 = new ReadThread("rt2", rrwLock);
        WriteThread wt1 = new WriteThread("wt1", rrwLock);
        rt1.start();
        rt2.start();
        wt1.start();
    }
}

参考文章:https://www.pdai.tech/md/interview/x-interview.html#_3-6-juc%E9%9B%86%E5%90%88%E7%B1%BB

外传

😜 原创不易,如若本文能够帮助到您的同学
🎉 支持我:关注我+点赞👍+收藏⭐️
📝 留言:探讨问题,看到立马回复
💬 格言:己所不欲勿施于人 扬帆起航、游历人生、永不言弃!🔥

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

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

相关文章

人体传感器SR501控制继电器

人体传感器SR501 原理 红外热释电检测移动人体 缺点 只能识别移动人体(静止的不行) 容易误判 正面 背面电路 跳线 H:触发周期可重复触发,一般选用此 L:不可重复触发,关掉之后才会重新触发 封锁周期 …

windows目录共享

开启SMB 1.0/CIFS服务器 打开控制面板 将 “SMB 1.0/CIFS文件共享支持” 这个勾上,点击确定。 选中一个要共享的文件夹,右键“属性”-->“共享”-->“高级共享” 勾上“共享次文件夹”,点击“权限” “组或用户名”选择“Everyone”,权…

idea将java程序打包为jar

idea将java程序打包为jar 灵光一现: 用java拉起浏览器,打开指定的网络地址,省的手动打开浏览器再复制地址过去了 本文记录了使用idea将java程序打包为jar包的过程 源码地址:https://gitcode.net/qq_39339588/jar.git 文章目录 ide…

Conmi的正确答案——Cordova安装并编译Android

系统:debian 11 Cordova版本:11.1.0 Cordova的Android平台:10.1.2 当前安卓最新稳定API:33(Android版本列表) 1、安装npm(cordova是基于nodejs开发的) apt install npm -y2、使用n…

如何在线制作思维导图?(普通制作流程)

不得不说,网上有很多可在线制作思维导图的工具,今天想给大家推荐分享一款非常好用的工具:ProcessOn思维导图 使用ProcessOn思维导图软件在线制作思维导图非常简单,只需要按照以下步骤即可: 打开ProcessOn官网 2.在P…

【c语言】组件化打包—静态库lib

创作不易&#xff0c;本篇文章如果帮助到了你&#xff0c;还请点赞 关注支持一下♡>&#x16966;<)!! 主页专栏有更多知识&#xff0c;如有疑问欢迎大家指正讨论&#xff0c;共同进步&#xff01; &#x1f525;c语言系列专栏&#xff1a;c语言之路重点知识整合 &#x…

项目质量体系搭建

质量意识 引入两个问题&#xff1a; 1、没有bug&#xff0c;算不算高质量&#xff1f; 2、没有bug&#xff0c;并且满足用户的需求&#xff0c;算不算高质量&#xff1f; 质量的认知 说起“质量”这个概念&#xff0c;我们都很熟悉&#xff0c;会说“坏的质量会怎样怎样&…

项目经理:靠学不靠干,绝对出不来

有人说&#xff1a;“项目经理是干出来的&#xff0c;不是学出来的&#xff1b;是带出来的&#xff0c;不是教出来的”。 我很赞同这个观点&#xff0c;要成为一名合格的项目经理不仅靠学&#xff0c;还要靠干。靠干&#xff0c;完全不学&#xff0c;可以出项目经理。但靠学不…

磁盘分析工具 WizTree

要点&#xff1a; 推荐两个应用&#xff1a;WizTree&#xff0c; SpaceSniffer.exe 参考资料&#xff1a;电脑软件&#xff1a;推荐一款磁盘空间分析工具——WizTree 1、WizTree是啥&#xff1f; WizTree 是一款Windows下磁盘空间分析工具。它可以快速扫描并分析你的电脑硬盘…

chatgpt赋能Python-python3_5__1

Python35<<1是什么&#xff1f;——深入探究Python3的位运算符 Python35<<1是一种使用Python编程语言实现的位运算操作。在计算机科学中&#xff0c;位运算符是用来对二进制数进行操作的&#xff0c;这种操作是以位为单位而不是以字节或字为单位。因此&#xff0c…

港联证券:机器人行业有望迎来整体性机会 六氟磷酸锂翻倍上涨

表示&#xff0c;当前AI调整的时间空间已接近13年水位&#xff0c;且调整的促发因素有望缓和&#xff0c;后续可积极一些。一方面&#xff0c;13年三次调整时间在40日以内、幅度在15%以内。当前AI调整已持续1个月、幅度在10%以上&#xff0c;时空已接近历史。另一方面&#xff…

JavaWeb-FilterListener的学习

Filter&Listener 1&#xff0c;Filter 1.1 Filter概述 Filter 表示过滤器&#xff0c;是 JavaWeb 三大组件(Servlet、Filter、Listener)之一。Servlet 我们之前都已经学习过了&#xff0c;Filter和Listener 我们今天都会进行学习。 过滤器可以把对资源的请求拦截下来&a…

MC6630: [ VI ] >热插拔摄像头如何处理

第一种情况, 之前是连上摄像头的, 现在拔下来: 当连着摄像头时: VI部分 通道属性的UserPic是不启用的, Irq是启用的. 拔下来后, 属性值不发生变化. 其它部分:各司其职, 正常运行 当断开时: VI部分不变: 对于VI来说, 就是没有图像源过来, 而VI通道而言: 有图像就处理,没有…

视频怎么转换成音频mp3?教你几种转换方法

视频怎么转换成音频mp3&#xff1f;MP3是一种有损压缩音频格式&#xff0c;全称为MPEG-1 Audio Layer 3。MP3格式可以在保证高质量的同时&#xff0c;采用比WAV更高效的压缩方式&#xff0c;降低文件大小。MP3格式广泛应用于数字音乐播放器、音频流媒体、网络广播等方面。虽然M…

常见的一些内网穿透工具

内网穿透的英文叫做 NAT traversal&#xff0c;又被称为端口映射或内网映射&#xff0c;内网穿透是网络连接术语&#xff0c;即在计算机是局域网内的时候&#xff0c;外网与内网的计算机的节点进行连接时所需要的连接通信&#xff0c;有时候就会出现内网穿透不支的情况。 内网穿…

全面评测安全企业邮箱加密服务,推荐高性价比提供商

安全电子邮件是加密形式的电子邮件。有权访问密钥的人只能阅读电子邮件。有许多安全的电子邮件发送工具可以避免业​​务风险并保护电子邮件中写入的信息。这些工具使您能够使用安全的端到端电子邮件加密来发送和接收消息。Zoho Mail企业邮箱最适合多用户帐户、小型企业和个人使…

从供应链协同角度挖掘数字化应用场景

企业在数字化转型的过程中&#xff0c;供应链的数字化转型是绕不开的话题。供应链的数字化转型&#xff0c;是借助数字化技术赋能企业和供应链从业人员&#xff0c;驱动业务向更加高效智能的方向发展。越来越多的企业意识到需要依靠新技术&#xff0c;也往往非常强调新技术的应…

数据结构与算法基础(青岛大学-王卓)(3)

第三弹来啦&#xff0c;第一章的顺序表和链表落下帷幕了&#xff0c;可以开开心心吃雪糕了:) beautiful分割线 文章目录 第三弹来啦&#xff0c;第一章的顺序表和链表落下帷幕了&#xff0c;可以开开心心吃雪糕了:)循环链表定义带尾指针循环链表的合并 双向链表定义双向循环链…

排版设计工具Affinity Publisher 2.04版本在win10系统上的下载与安装配置教程

目录 前言一、Affinity Publisher安装二、使用配置总结 前言 Affinity Publisher是一款由 Serif 公司开发的排版设计工具&#xff0c;旨在为用户提供完整的出版解决方案。 Affinity Publisher 工具的详细介绍&#xff1a; 1. 排版功能 Affinity Publisher 提供了各种排版功…

每日一练 | 软考网络工程师真题练习Day5

1、下面有关BGP4协议的描述中&#xff0c;不正确的选项是 。 A&#xff0e;BGP4是自治系统之间的路由协议 B&#xff0e;BGP4支持CIDR技术 C&#xff0e;BGP4把最正确通路参加路由表并通告邻居路由器 D&#xff0e;BGP4封装在TCP段中传送 2、CMP协议在网络中起到了过…