后端常问面经之并发

volatile 关键字

volatile关键字是如何保证内存可见性的?底层是怎么实现的?

"观察加入volatile关键字和没有加入volatile关键字时所生成的汇编代码发现,加入volatile关键字时,会多出一个lock前缀指令”lock前缀指令实际上相当于一个内存屏障(也成内存栅栏),内存屏障会提供3个功能: 1.它确保指令重排序时不会把其后面的指令排到内存屏障之前的位置,也不会把前面的指令排到内存屏障的后面;即在执行到内存屏障这句指令时,在它前面的操作已经全部完成; 2.它会强制将对缓存的修改提作立即写入主存: 3.如果是写操作,它会导致其他CPU中对应的缓存行无效。

所以可见性和禁止指令重排序如下: 可见性:volatile的功能就是被修饰的变量在被修改后可以立即同步到主内存,被修饰的变量在每次是用之前都从主内存刷新。 禁止指令重排序; volatile是通过内存屏障来禁止指令重排序JMM内存屏障的策略 在每个 volatile 写操作的前面插入一个 StoreStore 屏障,A StoreStore B,B的写操作不会重排序到A之前。 在每个 volatile 写操作的后面插入一个 StoreLoad 屏障,后面的读操作不会重排序到写操作之前。 在每个 volatile 读操作的后面插入一个 LoadLoad 屏障,后面的读操作不会重排序到读操作之前。 在每个 volatile 读操作的后面插入一个 LoadStore 屏障,后面的写操作不会重排序到读操作之前。

2. 如何禁止指令重排序?

双重校验锁实现对象单例(线程安全):

public class Singleton {
​private volatile static Singleton uniqueInstance;
​private Singleton() {}
​public  static Singleton getUniqueInstance() {//先判断对象是否已经实例过,没有实例化过才进入加锁代码if (uniqueInstance == null) {//类对象加锁synchronized (Singleton.class) {if (uniqueInstance == null) { uniqueInstance = new Singleton();}}}return uniqueInstance;}
}
​

uniqueInstance 采用 volatile 关键字修饰也是很有必要的, uniqueInstance = new Singleton(); 这段代码其实是分为三步执行:

  1. uniqueInstance 分配内存空间

  2. 初始化 uniqueInstance

  3. uniqueInstance 指向分配的内存地址

但是由于 JVM 具有指令重排的特性,执行顺序有可能变成 1->3->2。指令重排在单线程环境下不会出现问题,但是在多线程环境下会导致一个线程获得还没有初始化的实例。例如,线程 T1 执行了 1 和 3,此时 T2 调用 getUniqueInstance() 后发现 uniqueInstance 不为空,因此返回 uniqueInstance,但此时 uniqueInstance 还未被初始化。

3.volatile 可以保证原子性么?

volatile 关键字能保证变量的可见性,但不能保证对变量的操作是原子性的。

如何保证线程安全?

  • 使用线程安全的集合类,如ConcurrentHashMap、CopyOnWriteArrayList等。

  • 使用同步代码块(synchronized)或同步方法来保护共享资源,确保在同一时刻只有一个线程访问。

  • 使用Lock接口及其实现类(如ReentrantLock)来进行线程同步

  • 使用ThreadLocal来保证每个线程都有自己独立的变量副本

乐观锁和悲观锁

什么是悲观锁?

Java 中synchronizedReentrantLock等独占锁就是悲观锁思想的实现,共享资源每次只给一个线程使用,其它线程阻塞,用完后再把资源转让给其它线程。

什么是乐观锁?

乐观锁总是假设最好的情况,认为共享资源每次被访问的时候不会出现问题,线程可以不停地执行,无需加锁也无需等待,只是在提交修改的时候去验证对应的资源(也就是数据)是否被其它线程修改了(具体方法可以使用版本号机制或 CAS 算法)。

synchronized 关键字

synchronized 是什么?有什么用?

synchronized是Java中的一个关键字,主要解决的是多个线程之间访问资源的同步性,可以保证被他修饰的方法或者代码块在任意时刻只能有一个线程执行。

synchronized关键字的底层原理

synchronized 底层使用的JVM级别中的Monitor 来决定当前线程是否获得了锁,如果某一个线程获得了锁,在没有释放锁之前,其他线程是不能或得到锁的。synchronized 属于悲观锁。

synchronized 因为需要依赖于JVM级别的Monitor ,相对性能也比较低,因为涉及到了用户态和内核态的切换、进程的上下文切换,成本高,性能低。

介绍Monitor

monitor对象存在于每个Java对象的对象头中,synchronized 锁便是通过这种方式获取锁的,也是为什么Java中任意对象可以作为锁的原因

monitor内部维护了三个变量

  • WaitSet:保存处于Waiting状态的线程

  • EntryList:保存处于Blocked状态的线程

  • Owner:持有锁的线程

只有一个线程获取到的标志就是在monitor中设置成功了Owner,一个monitor中只能有一个Owner

在上锁的过程中,如果有其他线程也来抢锁,则进入EntryList 进行阻塞,当获得锁的线程执行完了,释放了锁,就会唤醒EntryList 中等待的线程竞争锁,竞争的时候是非公平的。

线程是怎么确定拿到锁的?

线程确定拿到锁的过程:是通过检查锁的状态并尝试获取锁来实现的。在JVM中,锁信息具体是存放在Java对象头中的。

当一个线程尝试进入synchronized代码块或方法时,JVM会检查对应对象的锁状态。如果对象的锁未被其他线程持有,即锁状态为可获取,那么该线程将成功获取锁并进入临界区执行代码。

synchronized 的锁升级

Java中的synchronized有偏向锁、轻量级锁、重量级锁三种形式,分别对应了锁只被一个线程持有、不同线程交替持有锁、多线程竞争锁三种情况。

重量级锁:底层使用的Monitor实现,里面涉及到了用户态和内核态的切换、进程的上下文切换,成本较高,性能比较低。

轻量级锁:线程加锁的时间是错开的(也就是没有竞争),可以使用轻量级锁来优化。轻量级修改了对象头的锁标志,相对重量级锁性能提升很多。每次修改都是CAS操作,保证原子性

偏向锁:一段很长的时间内都只被一个线程使用锁,可以使用了偏向锁,在第一次获得锁时,会有一个CAS操作,之后该线程再获取锁,只需要判断mark word中是否是自己的线程id即可,而不是开销相对较大的CAS命令

一旦锁发生了竞争,都会升级为重量级锁

如何使用 synchronized?

synchronized 关键字的使用方式主要有下面 3 种:

  1. 修饰实例方法(锁实例方法)

  2. 修饰静态方法(锁当前类)

  3. 修饰代码块(锁对象或者类)

synchronized 和 volatile 有什么区别?

volatile关键字主要解决变量在多个线程之间的可见性和和禁止指令重排序,synchronized关键字解决的是多个线程之间访问资源的同步性

ReentrantLock

ReentrantLock 是什么?

  • ReentrantLock实现了Lock接口,是一个可重入且独占式的锁,和synchronized关键字类似。

  • 不过,ReentrantLock更灵活、更强大、增加了等待可中断中断、公平锁和非公平锁、轮询、超时等高级功能。

public class ReentrantLock implements Lock, java.io.Serializable {}
​

ReentrantLock的底层原理

主要利用CAS+AQS队列来实现。它支持公平锁和非公平锁,两者的实现类似

构造方法接受一个可选的公平参数(默认非公平锁),当设置为true时,表示公平锁,否则为非公平锁。公平锁的效率往往没有非公平锁的效率高。

CAS和AQS

CAS的全称是: Compare And Swap(比较再交换);它体现的一种乐观锁的思想,在无锁状态下保证线程操作数据的原子性。

  • CAS使用到的地方很多:AQS框架、AtomicXXX类

  • 在操作共享变量的时候使用的自旋锁,效率上更高一些

  • CAS的底层是调用的Unsafe类中的方法,都是操作系统提供的,其他语言实现

AQS的话,其实就一个jdk提供的类AbstractQueuedSynchronizer,是阻塞式锁(和相关的同步器工具)的框架。

内部有一个属性 state 属性来表示资源的状态,默认state等于0,表示没有获取锁,state等于1的时候才标明获取到了锁。通过cas 机制设置 state 状态

在它的内部还提供了基于 FIFO 的等待队列,是一个双向列表,其中

  • tail 指向队列最后一个元素

  • head 指向队列中最前面的一个元素

其中我们刚刚聊的ReentrantLock底层的实现就是一个AQS。

ReentrantLock 与 synchronized对比

第一,语法层面

  • synchronized 是关键字,用 c++ 语言实现,退出同步代码块锁会自动释放

  • Lock 是接口,用 java 语言实现,需要手动调用 unlock 方法释放锁

第二,功能层面

  • 二者均属于悲观锁、都具备基本的互斥、同步、锁重入功能

  • Lock 提供了许多 synchronized 不具备的功能,例如获取等待状态、公平锁、可打断,同时Lock 可以实现不同的场景,如 ReentrantLock, ReentrantReadWriteLock

第三,性能层面

  • 在没有竞争时,synchronized 做了很多优化,如偏向锁、轻量级锁,性能不赖

  • 在竞争激烈时,Lock 的实现通常会提供更好的性能

公平锁和非公平锁

什么是公平锁和非公平锁?

  • 公平锁: 指多个线程按照申请锁的顺序来获取锁,线程直接进入队列中排队,队列中的第一个线程才能获得锁。公平锁的优点在于各个线程公平平等,每个线程等待一段时间后,都有执行的机会,而它的缺点就在于整体执行速度更慢,吞吐量更小。

  • 非公平锁: 多个线程加锁时直接尝试获取锁,能抢到锁到直接占有锁,抢不到才会到等待队列的队尾等待。非公平锁的优势就在于整体执行速度更快,吞吐量更大,但同时也可能产生线程饥饿问题,也就是说如果一直有线程插队,那么在等待队列中的线程可能长时间得不到运行。

非公平锁吞吐量为什么比公平锁大?

  • 公平锁执行流程:获取锁时,先将线程自己添加到等待队列的队尾并休眠,当某线程用完锁之后,会去唤醒等待队列中队首的线程尝试去获取锁,锁的使用顺序也就是队列中的先后顺序,在整个过程中,线程会从运行状态切换到休眠状态,再从休眠状态恢复成运行状态,但线程每次休眠和恢复(上下文切换)都需要从用户态转换成内核态(因为用户线程运行是在用户态,休眠调用的Thread.sleep()是系统调用),而这个状态的转换是比较慢的,所以公平锁的执行速度会比较慢。

  • 非公平锁执行流程:当线程获取锁时,会先通过 CAS 尝试获取锁,如果获取成功就直接拥有锁,如果获取锁失败才会进入等待队列,等待下次尝试获取锁。这样做的好处是,获取锁不用遵循先到先得的规则,从而避免了线程休眠和恢复的操作,这样就加速了程序的执行效率。

reentrantlock如何实现公平锁和非公平锁?

公平锁与非公平锁的 lock() 方法唯一的区别就在于公平锁在获取锁时多了一个限制条件:hasQueuedPredecessors() 为 false,这个方法就是判断在等待队列中是否已经有线程在排队了。这也就是公平锁和非公平锁的核心区别,如果是公平锁,那么一旦已经有线程在排队了,当前线程就不再尝试获取锁;对于非公平锁而言,无论是否已经有线程在排队,都会尝试获取一下锁,获取不到的话,再去排队。

ThreadLocal

ThreadLocal 有什么用?

ThreadLocal类位于java.lang包下,是JDK提供的一个类。TreadLocal类存放的是每个线程的私有数据,实现了线程之间的数据隔离,每个访问TreadLocal变量的线程都会有这个变量的本地副本。

ThreadLocal 原理了解吗?

img

  1. 每个Thread线程内部都有一个ThreadLocalMap,Map里面存储的是ThreadLocal对象作为key,线程的变量副本作为值。

  2. 当我们调用 set 方法,就是以 ThreadLocal 自己作为 key,资源对象作为 value,放入当前线程的 ThreadLocalMap 集合中

get 方法的主要流程为:

  • 先获取到当前线程的引用

  • 获取当前线程内部的 ThreadLocalMap

  • 如果 map 存在,则获取当前 ThreadLocal 对应的 value 值

  • 如果 map 不存在或者找不到 value 值,则调用 setInitialValue() 进行初始化

其中每个 Thread 的 ThreadLocalMap 以 threadLocal 作为 key,保存自己线程的 value 副本,也就是保存在每个线程中,并没有保存在 ThreadLocal 对象中。

set 方法的作用是把我们想要存储的 value 给保存进去。set 方法的流程主要是:

  • 先获取到当前线程的引用

  • 利用这个引用来获取到 ThreadLocalMap

  • 如果 map 为空,则去创建一个 ThreadLocalMap

  • 如果 map 不为空,就利用 ThreadLocalMap 的 set 方法将 value 添加到 map 中

ThreadLocal会导致内存溢出

是应为ThreadLocalMap 中的 key 被设计为弱引用,在内存不太够用的时候,只有key可以得到内存释放,而value不会,因为value是一个强引用。

在使用ThreadLocal 时都把它作为静态变量(即强引用),因此无法被动依靠 GC 回收,建议主动的remove 释放 key,这样就能避免内存溢出。

ThreadLocal的使用

数据的跨层使用(controller,service,dao)

每个线程保存类似全局变量的信息(例如拦截器中的用户信息),可以让不同层的方法直接使用,但不想被多线程共享,此时就可以用ThreadLocal保存业务的信息(例如用户id)。

Spring使用ThreadLocal解决线程安全问题

一般情况下,只有无状态的Bean才能在多线程环境下共享。对于有些有状态的Bean,将Bean中一些非线程安全的变量放在ThreadLocal中,使他们成为线程安全的Bean,就可以在多线程环境下共享了。并且在同一次请求响应的调用线程中,所有对象访问的同一个ThreadLocal对象都是当前线程所绑定的。

示例:

TopicDao:非线程安全,由于①处的conn是成员变量,因为addTopic()方法是非线程安全的,必须在使用时创建一个新TopicDao实例(非singleton)。下面使用ThreadLocal对conn这个非线程安全的“状态”进行改造:

创建线程的方式

方式一:继承Thread类并重写run()方法。

  • 优点: 编写简单,如果需要访问当前线程,无需使用Thread.currentThread ()方法,直接使用this,即可获得当前线程

  • 缺点:因为线程类已经继承了Thread类,所以不能再继承其他的父类

public class CreatingThread01 extends Thread {@Overridepublic void run() {System.out.println(getName() + " is running");}
​public static void main(String[] args) {new CreatingThread01().start();new CreatingThread01().start();new CreatingThread01().start();new CreatingThread01().start();}
}

方式二:实现Runnable接口并实现run()方法,然后将实现了Runnable接口的类传递给Thread类。

采用实现Runnable接口方式:

  • 优点:线程类只是实现了Runable接口,还可以继承其他的类。在这种方式下,可以多个线程共享同一个目标对象,所以非常适合多个相同线程来处理同一份资源的情况。

  • 缺点:编程稍微复杂,如果需要访问当前线程,必须使用Thread.currentThread()方法。

public class CreatingThread02 implements Runnable {@Overridepublic void run() {System.out.println(Thread.currentThread().getName() + " is running");}
​public static void main(String[] args) {new Thread(new CreatingThread02()).start();new Thread(new CreatingThread02()).start();new Thread(new CreatingThread02()).start();new Thread(new CreatingThread02()).start();}
}

方式三:使用Callable和Future接口通过Executor框架创建线程。

  • 优点:1. 线程类只是实现了Runable接口,还可以继承其他的类。 2. 在这种方式下,可以多个线程共享同一个目标对象,所以非常适合多个相同线程来处理同一份资源的情况。 3. 允许线程有返回值

  • 缺点:编程稍微复杂,如果需要访问当前线程,必须调用Thread.currentThread()方法。

public class CreatingThread03 implements Callable<Long> {@Overridepublic Long call() throws Exception {Thread.sleep(2000);System.out.println(Thread.currentThread().getId() + " is running");return Thread.currentThread().getId();}
​public static void main(String[] args) throws ExecutionException, InterruptedException {FutureTask<Long> task = new FutureTask<>(new CreatingThread03());new Thread(task).start();System.out.println("等待完成任务");Long result = task.get();System.out.println("任务结果:" + result);}
}

线程池

什么是线程池?

线程池就是管理一系列线程的资源池。当有任务要处理时,直接从线程池中获取线程来处理,处理完后线程并不会立即被销毁,而是等待下一个任务。

如何创建线程池?

方式一:

在jdk中默认提供了4中方式创建线程池

第一个是:newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回 收空闲线程,若无可回收,则新建线程。

第二个是:newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列 中等待。

第三个是:newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。

第四个是:newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任 务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

方式二:通过ThreadPoolExecutor构造函数来创建(推荐)。

在线程池中一共有7个核心参数:

  • corePoolSize:线程池核心线程数量。默认情况下,线程池中线程的数量如果 <= corePoolSize,那么即使这些线程处于空闲状态,那也不会被销毁。

  • maximumPoolSize:线程池中最多可容纳的线程数量。当一个新任务交给线程池,如果此时线程池中有空闲的线程,就会直接执行,如果没有空闲的线程且当前线程池的线程数量小于corePoolSize,就会创建新的线程来执行任务,否则就会将该任务加入到阻塞队列中,如果阻塞队列满了,就会创建一个新线程,从阻塞队列头部取出一个任务来执行,并将新任务加入到阻塞队列末尾。如果当前线程池中线程的数量等于maximumPoolSize,就不会创建新线程,就会去执行拒绝策略。

  • keepAliveTime:当线程池中线程的数量大于corePoolSize,并且某个线程的空闲时间超过了keepAliveTime,那么这个线程就会被销毁。

  • unit:就是keepAliveTime时间的单位。

  • workQueue:工作队列。当没有空闲的线程执行新任务时,该任务就会被放入工作队列中,等待执行。

  • threadFactory:线程工厂。可以用来给线程取名字等等

  • handler:拒绝策略。当一个新任务交给线程池,如果此时线程池中有空闲的线程,就会直接执行,如果没有空闲的线程,就会将该任务加入到阻塞队列中,如果阻塞队列满了,就会创建一个新线程,从阻塞队列头部取出一个任务来执行,并将新任务加入到阻塞队列末尾。如果当前线程池中线程的数量等于maximumPoolSize,就不会创建新线程,就会去执行拒绝策略。

如何自定义线程池?

要自定义线程池,可以使用 ThreadPoolExecutor 类。以下是创建自定义线程池的步骤:

  1. 创建一个 ThreadPoolExecutor 对象,可以通过构造方法来指定核心线程数、最大线程数、线程空闲时间、任务队列等参数。

  2. 可以通过调用 execute(Runnable command) 方法向线程池提交任务。

  3. 可以通过调用 shutdown() 方法关闭线程池。

示例代码如下:

ThreadPoolExecutor threadPool = new ThreadPoolExecutor(5, // 核心线程数10, // 最大线程数60, // 线程空闲时间TimeUnit.SECONDS, // 时间单位new ArrayBlockingQueue<>(100) // 任务队列
);
​
threadPool.execute(() -> {// 执行任务逻辑
});
​
threadPool.shutdown();

线程池处理任务的流程了解吗?

线程池分为核心线程池,线程池的最大容量,还有等待任务的队列,提交一个任务,如果核心线程没有满,就创建一个线程,如果满了,就是会加入等待队列,如果等待队列满了,就会增加线程,如果达到最大线程数量,如果都达到最大线程数量,就会按照一些丢弃的策略进行处理。

为什么核心线程满了之后是先加入阻塞队列而不是直接加到总线程?

  • 线程池创建线程需要获取mainlock这个全局锁,会影响并发效率,所以使用阻塞队列把第一步创建核心线程与第三步创建最大线程隔离开来,起一个缓冲的作用。

  • 引入阻塞队列,是为了在执行execute()方法时,尽可能的避免获取全局锁。

核心线程数一般设置为多少?

假设机器有N个CPU:

  • 如果是CPU密集型应用,则线程池大小设置为N+1,线程的应用场景:主要是复杂算法

  • 如果是IO密集型应用,则线程池大小设置为2N+1,线程的应用场景:主要是:数据库数据的交互,文件上传下载,网络数据传输等等

对于同时有计算工作和IO工作的任务,应该考虑使用两个线程池,一个处理计算任务,一个处理IO任务,分别对两个线程池按照计算密集型和IO密集型来设置线程数。

IO密集型的线程数为什么一般设置为2N+1?

在IO密集型任务中,线程通常会因为IO操作而阻塞,此时可以让其他线程继续执行,充分利用CPU资源。设置为2N+1可以保证在有多个线程阻塞时,仍有足够的线程可以继续执行。

为什么不建议使用Executors创建线程池呢

如果使用Executors创建线程池的话,它允许的请求队列默认长度是Integer.MAX_VALUE,这样的话,有可能导致堆积大量的请求,从而导致OOM(内存溢出)。

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

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

相关文章

2015年认证杯SPSSPRO杯数学建模D题(第二阶段)城市公共自行车全过程文档及程序

2015年认证杯SPSSPRO杯数学建模 D题 城市公共自行车 原题再现&#xff1a; 城市交通问题直接影响市民的生活和工作。在地形平坦的城市&#xff0c;公共自行车出行系统是一种很好的辅助手段。一般来说&#xff0c;公共自行车出行系统由数据中心、驻车站点、驻车桩、自行车&…

吴恩达深度学习笔记:浅层神经网络(Shallow neural networks)3.1-3.5

目录 第一门课&#xff1a;神经网络和深度学习 (Neural Networks and Deep Learning)第三周&#xff1a;浅层神经网络(Shallow neural networks)3.1 神经网络概述&#xff08;Neural Network Overview&#xff09;3.2 神经网络的表示&#xff08;Neural Network Representation…

2024年碑林区软科学研究项目申报类型方向、时间流程要求

一、征集类型及研究方向 项目类型:重点支持综合运用自然科学、社会科学和工程技术多门类、多学科知识,为科技和经济社会发展的重大决策提供支撑。 项目支持以“五大发展理念”为指导,围绕科技、经济、民生等社会发展问题,开展对策研究,包含但不限于:加快培育新质生产力、秦创…

【Java八股面试系列】中间件-Redis

目录 Redis 什么是Redis Redis解决了什么问题 Redis的实现原理 数据结构 String 常用命令 应用场景 List(列表) 常用命令 应用场景 Hash(哈希) 常用命令 应用场景 set(集合) 常见命令​编辑 应用场景 Sorted Set(有序集合) 常见命令​编辑 应用场景 数据持…

idea中gradle编译下运营main方法报CreateProcess error=206

问题描述 CreateProcess error206 文件名或扩展名太长 问题分析 解决方案 build.gradle文件里添加 buildscript {repositories {maven {url "https://plugins.gradle.org/m2/"}}dependencies {classpath "gradle.plugin.ua.eshepelyuk:ManifestClasspath:1.…

kali MSF网络安全框架

MSF&#xff0c;全称The Metasploit Framework&#xff0c;简称MSF&#xff0c;是一个强大的网络安全框架&#xff0c;主要用于进行渗透测试和漏洞利用。它提供了丰富的模块和工具&#xff0c;帮助安全研究人员和渗透测试人员发现、利用和修复系统中的安全漏洞。 操作: 不夸张…

web渗透测试漏洞流程:红队资产信息收集之单个目标信息收集

端口爆破工具用法 Hydra 参数详解: -R 根据上一次进度继续破解 -S 使用SSL协议连接 -s 指定端口 -l 指定用户名 -L 指定用户名字典(文件) -p 指定密码破解 -P 指定密码字典(文件) -e 空密码探测和指定用户密码探测(ns) -M <FILE>指定目标列表文件一行一条 -…

Pandas操作MultiIndex合并行列的Excel,写入读取以及写入多余行及Index列处理,插入行,修改某个单元格的值

Pandas操作MultiIndex合并行列的excel&#xff0c;写入读取以及写入多余行及Index列处理 1. 效果图及问题2. 源码参考 今天是谁写Pandas的 复合索引MultiIndex&#xff0c;写的糊糊涂涂&#xff0c;晕晕乎乎。 是我呀… 记录下&#xff0c;现在终于灵台清明了。 明天在记录下直…

【Python百日进阶-Web开发-Peewee】Day287 - Peewee 的扩展(六)字段等

文章目录 13.10 字段class CompressedFieldclass PickleField 13.11 混合属性 Hybrid Attributes13.11.1 混合 APIclass hybrid_methodclass hybrid_property 13.12 键/值存储 Key/Value Storeclass KeyValue 13.10 字段 这些字段可以在playhouse.fields模块中找到。 class C…

Ubuntu22.04安装WordPress教程

Ubuntu22.04安装LEMP堆栈(Nginx MariaDB PHP)教程 Ubuntu22.04安装WordPress教程&#xff08;利用nginx环境和MariaDB数据库&#xff0c;安装使用WordPress&#xff09; 本教程将展示如何在 Ubuntu22.04 上安装 LEMP 堆栈。 一个软件堆栈是捆绑在一起的一组软件工具。 LEMP …

华为全套企业管理资料合集(21专题)

华为全套企业管理资料合集-知识星球下载 1.绩效考核 华为内训绝密资料:绩效管理与绩效考核.ppt 华为绩效管理与绩效考核制度.docx 华为公司实用性各种绩效图表汇总.doc 华为公司考勤管理制度.doc 华为IPD模式中跨部门团队成员的考核激励制度.doc 2.企业管理 华为公司人力资源…

NetCore itext7 创建、编辑PDF插入表格、图片、文字(三)

NetCore 创建、编辑PDF插入表格、图片、文字 NetCore 创建、编辑PDF插入表格、图片、文字(二) NetCore 创建、编辑PDF插入表格、图片、文字(三) 直接上代码 nuget引入 itext7 using System; using System.IO;using iText.IO.Image; using iText.Kernel.Colors; // 导入颜色…

【Spring】IoCDI详解

1. IoC详解 前面提到过IoC就是将对象的控制权交由Spring的IoC容器进行管理&#xff0c;由Spring的IoC容器创建和销毁bean&#xff0c;那么既然涉及到容器&#xff0c;就一定包含以下两方面功能&#xff1a; bean的存储bean的获取 1.1 类注解 Spring框架为了更好地服务应用程…

7-30 三天打鱼两天晒网

中国有句俗语叫“三天打鱼两天晒网”。假设某人从某天起&#xff0c;开始“三天打鱼两天晒网”&#xff0c;问这个人在以后的第N天中是“打鱼”还是“晒网”&#xff1f; 输入格式&#xff1a; 输入在一行中给出一个不超过1000的正整数N。 输出格式&#xff1a; 在一行中输…

视频中有无声音的检测

最近遇到一个烦心的事&#xff0c;晚上车停在路边车窗被砸了&#xff0c;行车记录仪正好没安装好&#xff0c;没有拍到&#xff0c;需要对视频声音进行分析确定被砸时间&#xff0c;但我的行车记录仪是每一分钟拍一个视频&#xff0c;一晚上的视频非常多&#xff0c;听起来非常…

树梅派Raspberry Pi OS(Debian)源码交叉编译升级内核参数PAGESIZE

树梅派Raspberry Pi OS(Debian)源码交叉编译升级内核参数PAGESIZE 环境&#xff1a; device: Raspberry Pi 3 Model B Rev 1.2 os: 2024-03-15-raspios-bookworm-arm64 (Debian GNU/Linux 12 bookworm aarch64) kernel: 6.6.20rpt-rpi-v8 arch: aarch64 编译机: ubuntu 22.0…

高效物联网连接技术创新:ECWAN边缘协同自组网的未来——基于ChirpLAN窄带扩频技术的无线混合组网

物联网是指将各种物理设备通过互联网进行连接和通信的技术。它是一个庞大的网络&#xff0c;由传感器、设备、网络和云服务组成&#xff0c;旨在实现对物体的远程监测、控制和数据采集。 基于ChirpLAN窄带扩频技术的无线混合组网协议ChirpLAN&#xff0c;ChirpLAN是基于其自有的…

每天学习一个Linux命令之hostname

每天学习一个Linux命令之hostname 简介 hostname命令用于显示或设置系统的主机名。主机名是在网络环境中使用的标识符&#xff0c;可用于识别和定位服务器或设备。 基本语法 hostname [选项] [新主机名]可用选项 hostname命令有一些常用的选项&#xff0c;以下是它们的详细…

Itextpdf电子签章

印章 印章是我国特有的历史文化产物&#xff0c;古代主要用作身份凭证和行驶职权的工具。它的起源是由于社会生活的实际需要。早在商周时代&#xff0c;印章就已经产生。如今的印章已成为一种独特的&#xff0c;融实用性和艺术性为一体的艺术瑰宝。传统的印章容易被坏人、小人…

久菜盒子|毕业设计|金融|DCC-GARCH模型

在R语言中&#xff0c;提到“DCC(1,1)”通常是指使用Dynamic Conditional Correlation (DCC)模型对一组金融资产收益率之间的动态相关性进行建模。DCC模型是GARCH族模型的一个变种&#xff0c;特别适用于处理多元时间序列数据中的条件相关结构。它由Robert Engle等人提出&#…