并发编程入门指南

news2024/12/24 11:30:00

文章目录

  • 并发编程
    • 进程和线程的区别
    • 并发和并行的区别
    • 创建线程的方式
    • 线程之间的状态,状态之间的转换
    • 新建三个线程,如何保证按顺序执行
    • wait方法和sleep的区别
    • 如何停止一个正在运行的线程
    • synchronized关键字底层原理
    • Monitor属于重量级锁,了解过锁升级吗
    • JMMJava内存模型
    • CAS(Compare And Swap)自旋锁
    • 乐观锁和悲观锁的区别
    • volatile关键字
    • AQS(AbstractQueuedSynchronizer)
    • 什么是公平锁和非公平锁?
    • ReentrantLock实现原理
    • synchronized和Lock的区别
    • 可重入锁
    • 死锁产生的条件
    • Java并发程序出现问题的原因以及解决方法
    • 为什么使用线程池
    • 线程池的核心参数以及执行原理
    • 线程池中常见的阻塞队列

并发编程

进程和线程的区别

  • 进程就是运行一个程序,程序是由指令和数据组成,程序要运行,就需要将指令加载到CPU中,数据加载到内存中,进程就是将指令加载到CPU中,并且将数据加载到内存中,并且指令运行期间还会用到磁盘、网络等设备。线程的话就是一个指令流,线程的运行就是将指令流中的一条条指令交给CPU执行。而进程就是由一个个线程构成,每一个线程执行不同的功能。
  • 不同的进程使用不同的内存空间,同一个进程中的线程共享内存空间。
  • 线程更轻量级,线程间的上下文切换(一个线程切换到另一个线程)成本要比进程上下文切换成本低。

并发和并行的区别

  • 并发就是单核CPU加载线程的过程中是一个线程执行结束然后去执行另一个线程,但是由于速度非常快感觉像是同时进行,微观串行,宏观并行,这种线程轮流使用CPU的做法就称为并发(concurrent)。
  • 并行就是多核CPU在加载线程的过程中,每个核心都可以同时去加载线程,这种同一时间去执行多个线程的方式称为并行。
  • 举个例子,一个保姆需要做饭、打扫卫生、接孩子,她一个人轮流交替做这些,就是并发;这时候又雇了一个保姆,两个人同时完成这些事情,这就是并行。

创建线程的方式

  • 创建线程主要有四种方式,第一种是集成Thread类,然后重写run方法,然后使用start进行启动。第二种是实现Runnable接口,重写run方法,创建这个类的对象,然后创建Thread时将这个对象传入,使用start启动线程。第三种是实现Callable接口,重写call方法(用于返回最后执行线程拿到的结果),需要传入一个泛型,然后创建这个类的对象,这个时候就要创建一个FutureTask类将这个对象传入,然后创建Thread时将创建的FutureTask传入,使用start启动线程。第四种直接使用线程池创建线程,实现Runnable接口,重写run方法,然后创建线程池对象,使用submit将创建的线程提交。
  • Runnable和Callable两者的主要区别是,Runnable的run方法没有返回值,Callable的call方法有返回值,并且是泛型,可以和Future、FutureTask配合获取异步执行结果。还有就是Callable接口的call方法允许抛出异常,而Runnable的run方法异常只能内部消化,不能向上抛出。
  • .run.start的区别,run是正常的执行run方法,可以执行多次,而start是用来启动线程,一个线程只能启动一次,start方法只能调用一次。

线程之间的状态,状态之间的转换

  • 线程主要有六种状态,新建(new),可运行(runnable),阻塞(blocked),等待(waiting),时间等待(timed_waiting),终止(terminated)
  • 当创建一个线程对象的时候是新建状态,当调用start之后就会变为可执行状态,在可执行状态下如果获取到CPU资源就立即执行,并且线程会终止,如果在可执行状态中没有获取到CPU权限时可能会切换到其他状态,比如如果没有获取到锁,就会进入阻塞状态,如果获得锁就会切换到可运行状态,如果线程调用了wait方法就会进入等待状态,只有当其他线程调用notify唤醒后就会切换为可执行状态,如果线程调用了sleep方法,线程就会进入时间等待状态,到时间之后自动切换为可执行状态。

新建三个线程,如何保证按顺序执行

  • 可以使用线程中的join方法解决,阻塞调用此方法的线程进入timed_waiting,比如t.join()直到被调用的线程t执行完成,当前线程才进入runnable状态。
  • 三个线程t1,t2,t3想要按顺序执行,这时候就需要在t2线程中使用t1.join(),在t3线程中使用t2.join();

wait方法和sleep的区别

  • 两者的相同点是都可以让当前线程放弃CPU的使用权,进入阻塞状态。
  • 不同点首先是方法的归属不同,sleep(long)是Thread中的静态方法,wait和wait(long)是Object中的成员方法,每个对象中都会有。
  • 其次醒来的时机不同,两者进入runnable的时机不同,sleep(long)、wait(long)这两个都是在等待longms之后就自动进入runnable状态,wait(long)和wait这两者可以被notify唤醒直接进入runnable状态,wait如果没有notify唤醒会一直等待下去,他们都可以被打断唤醒。
  • 然后这两者的锁性质不同,wait方法必须配合synchronized锁来使用,当使用wait方法放弃CPU时,相应的锁也会释放,其他线程可以获得该对象锁,而sleep如果在synchronized中执行放弃CPU时,不会释放对象锁。

如何停止一个正在运行的线程

  • 线程正常运行,然后使用退出标志,线程正常退出,也就是线程执行完run方法之后自动终止。
  • 使用stop方法强行退出终止,但是目前已经弃用。
  • 使用interrupt方法中断线程,如果打断了阻塞状态的线程,那么就会抛出异常,当调用interrupt方法的时候,相当于将isInterrupt的返回值改为了true,如果是打断了正常运行状态的线程,相当于使用了退出标签正常退出了当前的线程。

synchronized关键字底层原理

  • synchronized(对象锁)采用的互斥的方式,使得同一时刻只有一个线程可以持有。
  • synchronized的底层是由monitor实现的,线程获取锁需要使用锁关联monitor。在monitor中有三个属性,owner、entrylist、waitset。其中owner关联的是获得锁的线程,并且只能关联一个线程,entrylist关联的是处于blocked状态的线程,而waitset关联的是出于waiting状态的线程。

Monitor属于重量级锁,了解过锁升级吗

  • Java中synchronized主要有偏向锁、轻量级锁、重量级锁三种形式,对应的是一个线程持有,不同线程交替持有锁,多个线程竞争锁的情况。
  • 在JVM中,一个对象锁在内存区域分为三个部分,对象头(mark word)、实例数据(objectbody)、对齐填充0,markword用于存储锁自身运行时的数据,比如hashcode、GC分代年龄、锁状态标志、线程持有的锁、偏向线程id等。
  • 底层使用的Monitor是重量级锁,重量级锁里面涉及到了用户态和内核态的切换、进程的上下文转换,成本较高并且性能较低。
  • 线程加锁的时间是错开的(没有竞争),这种可以使用轻量级锁。轻量级锁优化了对象头的锁标志,相比重量级锁的性能提升了很多。但是每次修改都需要CAS操作,用来保证原子性。
  • 一段很长的时间只被一个线程使用锁,就可以使用偏向锁,偏向锁只会在第一次获取锁的时候进行CAS操作,之后线程再去获取锁的时候,只需要判断mark word中的线程id是否是自己的即可,而不是开销大的CAS命令。
  • 当系统调用重量级锁之后,就会将重量级锁指向Monitor这个结构
    markword的标识

JMMJava内存模型

  • JMM是一套规范,定义了多线程程序在执行的时候,内存中的各个变量、对象以及执行顺序等行为。
  • JMM将内存分为两块,一块是线程私有的工作区域(工作内存),一块是所有线程共享的区域(主内存)。
  • 线程和线程之间是相互隔离的,线程和线程的交互需要通过主内存。

在这里插入图片描述

CAS(Compare And Swap)自旋锁

while(true){
	int 旧值 = 共享变量值;
	int 结果 = 旧值 ++;
	if(compareAndSwap(旧值, 结果)){
		break;
	}
}
  • CAS的一个思想就是当一个线程需要操作共享内存中的数据的时候需要先判断修改前的数据与共享内存中的数据是否一致,如果不一致则重新获取共享内存中的数据,然后进行操作,然后再判断是否一致,一致的时候可以进行替换操作。
  • CAS体现的是一种乐观锁的思想,在无锁状态下保证线程的原子性。
  • 在操作共享变量的时候使用CAS,效率上更高。
  • CAS底层调用的Unsafe类中的方法,是操作系统提供的,是由其他语言实现的。

乐观锁和悲观锁的区别

  • 乐观锁就是乐观的认为并发操作不加锁的方式实现是没有任何问题的,每次操作前进行判断(CAS)是否成立,不加锁的实现。
  • 悲观锁认为对同一个数据进行并发操作一定是会发生修改的,即使没有修改也会认为修改过,认为并发操作一定是有问题的,必须加锁,synchronized是基于悲观锁的思想的。

volatile关键字

  • 当使用volatile修饰一个共享变量(成员变量,静态成员变量),这个共享变量就具备了两层含义,保证进程之间的可见性、禁止进行指令重排序
  • 使用volatile修饰一个共享变量,能够防止编译器等优化发生,让一个线程对共享变量的修改对另一个线程可见。比如现在有一个程序,其中有一个boolean类型的共享变量,线程a修改了这个boolean类型的值,但是另一个线程b在while循环中使用boolean进行循环判断,此时由于JVM中的JIT编译器对这个while循环进行了优化,将while(!flag)变为了while(true),此时就算修改了这个flag,线程b同样还在执行循环,这种情况下有两种解决方式,第一种就是在程序运行时候加入vm参数-Xint,禁用掉JIT,但是会将其他程序的也禁用掉,不推荐;第二种方式就是在修饰这个flag共享变量时候加上volatile关键字,也就是告诉JIT,不要对voletile修饰的变量做优化。
  • 禁止指令重排序的原理是使用了内存屏障,在同一线程中,不同变量之间的读写顺序可能会不一致,这时候修饰属性的时候添加volatile关键字就可以为其添加一层内存屏障,添加内存屏障后,这个变量在进行写操作的时候其他在它之前的变量不能在其之后进行写操作,在进行读操作的时候其它之后的变量不能在其之前进行读操作。

AQS(AbstractQueuedSynchronizer)

  • AQS即抽象队列同步器,是JUC中提供的一种锁机制,它是构建锁或者其他同步组件的基础框架。JUC中很多关于锁的类底层都是基于AQS来实现的。
  • synchronized和AQS的区别,synchronized是关键字底层是C++实现,AQS是Java语言实现的;两者都是悲观锁,synchronized锁是自动创建和释放,AQS是手动创建和释放;synchronized在锁竞争激烈的时候会自动升级为重量级锁,性能较差,AQS在锁竞争激烈的时候会有多种解决方式。
  • 在AQS内部维护了一个state变量,有0和1两种状态,当没有线程访问时是0,当有线程访问这个锁的时候,会将这个state变为1,这时候如果有其他线程想要访问,此时因为state为1,所以访问失败,在AQS内部还维护了一个队列,此时这个访问失败的线程就会进入队列中等待锁释放,后面的线程同理,当持有锁的线程执行完成的时候,state变为0,同时会唤醒队列中的头元素,让它去持有锁。
  • 如果在唤醒头元素的过程中来了两个线程进行抢占,这时候使用CAS来保证state的原子性,当有一个线程抢占到锁,然后将其state变为1,其他线程就会进入队列。
  • AQS是非公平锁,因为当唤醒头元素的过程中来了个线程进行抢占,这时候如果抢占成功是对队列中其他线程是不公平的。只有当新来的线程直接进入队列进行等待,才是公平锁。

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

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

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

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

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

ReentrantLock实现原理

  • ReentrantLock是支持可重入锁,也就是调用lock方法后还可以继续调用,不会阻塞。
  • ReentrantLock主要利用CAS+AQS队列来实现的。在创建ReentrantLock锁的时候构造方法的内部会创建一个默认的NonfairSync而这个NonfairSync底层继承的就是AQS,在NonfairSync中也有一个state用来记录线程锁是否被占用,里面还维护了一个双向队列,头节点head和尾节点tail,还有一个exclusiveOwnerThread属性,用于指向获取锁成功的线程。
  • 支持公平锁和非公平锁。在创建ReentrantLock的时候默认的构造器是非公平锁,但是我们可以通过传参(true or false)构造器创建公平锁。但是公平锁的效率往往是没有非公平锁高的。

synchronized和Lock的区别

  • synchronized是关键字,底层是基于JVM实现的,Lock是接口,是Java中的JDK提供的。在使用synchronized的时候会自动释放锁,而使用Lock时需要手动调用unlock方法释放锁。
  • 两者都是属于悲观锁,都具备基本的互斥、同步、可重入功能。Lock提供了多种synchronized不具备的功能,比如公平锁、可打断(lockInterruptibly)、可超时(tryLock)、多条件变量(Condition)。
  • 在性能上,在锁竞争不是很激烈的时候,synchronized做了非常多的优化,像是偏向锁、轻量级锁等;但是当锁竞争激烈的时候synchronized就会转为重量级锁,性能大幅度下降,而Lock通常会提供更好的性能实现。

可重入锁

可重入锁是指同一个线程在获取了锁之后,可以再次重复获取该锁而不会造成死锁或其他问题。当一个线程持有锁时,如果再次尝试获取该锁,就会成功获取而不会被阻塞。
ReentrantLock实现可重入锁的机制是基于线程持有锁的计数器。

  • 当一个线程第一次获取锁时,计数器会加1,表示该线程持有了锁。在此之后,如果同一个线程再次获取锁,计数器会再次加1。每次线程成功获取锁时,都会将计数器加1。
  • 当线程释放锁时,计数器会相应地减1。只有当计数器减到0时,锁才会完全释放,其他线程才有机会获取锁。
    这种计数器的设计使得同一个线程可以多次获取同一个锁,而不会造成死锁或其他问题。每次获取锁时,计数器加1;每次释放锁时,计数器减1。只有当计数器减到0时,锁才会完全释放。
    ReentrantLock通过这种计数器的方式,实现了可重入锁的机制。它允许同一个线程多次获取同一个锁,并且能够正确地处理锁的获取和释放,避免了死锁和其他并发问题。

死锁产生的条件

  • 当一个线程在执行过程中需要同时获取多把锁,这时候容易发生死锁。比如线程1持有a锁等待获取b锁,线程2持有b锁等待获取a锁。
  • 如果出现了死锁,我们可以通过idea下的Terminal,通过jps和jstack来进行诊断,使用jps输出JVM中运行的进程状态信息,使用jstack查看Java进程内线程的堆栈信息、查看日志,并检查线程中是否有死锁。还可以通过JDK下的可视化工具jconsole、VisualVM来检查死锁问题。

Java并发程序出现问题的原因以及解决方法

  • 原子性问题,原子性就是一个线程在CPU执行过程中要么全部执行完成,要么不执行,不可以中断。并发中出现这种问题可以通过加锁解决,使用synchronized或Lock。
  • 可见性,在一个线程修改共享内存中的数据的时候,另一个线程必须同步这个共享内存中的数据。可以使用加锁解决,但是性能方面不太好,可以加volatile关键字解决。
  • 有序性问题的话可以使用volatile解决,volatile里面有内存屏障,可以方式变量在其他变量之前或之后优先读写。

为什么使用线程池

  • 我们在创建线程的过程中都会占用一定的内存空间,如果无限的创建线程就有可能浪费内存,严重的话会导致内存溢出。
  • 我们CPU一般只有一个,当如果有大量线程进行创建,很多线程没有CPU的执行权,那这些线程都得进行等待,会造成大量线程之间的切换,也会导致性能变慢。

线程池的核心参数以及执行原理

在这里插入图片描述

  • 核心线程数目是线程中主要执行任务的数目,第二个是最大线程的数目=核心线程数目+临时线程最大数目,第三个生存时间就是临时线程的生存时间,如果在生存时间内没有新任务,那么此线程资源会释放,第四个时间单位就是生存时间的单位,如秒、毫秒等,第五个阻塞队列,当没有空闲的核心线程时,新来的任务会加入到此队列排队,队列满了会创建临时线程执行任务,第六个线程工厂可以用来定制线程对象的创建,比如设置线程的名字、是否守护线程等,第七个参数拒绝策略,当所有的线程都在繁忙,核心线程和临时线程都在忙,阻塞队列 也满了,就会触发拒绝策略。
  • 线程池的执行原理是,当一个线程创建进入线程池,这个任务会首先提交给核心线程判断是否已满,如果未满则使用核心线程进行执行,如果已满则去判断阻塞队列是否已满,如果阻塞队列未满,则将这个任务添加到阻塞队列的任务队列,如果已满,则去判断当前线程数目是否小于最大线程数目,如果小于最大线程数,则会创建临时线程去执行任务(一般当核心线程和临时线程有一方为空就回去判断阻塞队列中是否为空,不为空就将其中的任务调出执行),如果已满,则会触发拒绝策略进行处理。
  • 拒绝策略的处理一般分为四种,第一种是默认的处理方法返回异常(AbortPolicy);第二种是用调用者所在线程来执行任务(CallerRunsPolicy),也就是使用当前正在执行的线程来执行这个任务(与线程池无关);第三种是丢弃阻塞队列中最靠前的任务(DiscardOldestPolicy),并执行当前任务;第四种是直接丢弃当前任务(DiscardPolicy)。

线程池中常见的阻塞队列

  • 常见的阻塞队列会有四种,基于数组的有界阻塞队列(ArrayBlockingQueue),基于链表的有界阻塞队列(LinkedBlockingQueue),还有一个优先队列(DelayedWorkQueue),可以保证每次出队都是当前队列中时间最靠前的,还有一种是不存储元素的阻塞队列(SynchronousQueue),每个插入操作都必须等待一个移出操作。
  • 最常见的主要是前两种,一种是基于数组有界的阻塞队列,这种在创建的时候底层会创建一个数组,当你传入容量时会设置数组长度,否则会默认设置为Integer.MAX_VALUE,它的底层只有一把锁,出队入队都要使用这把锁,效率较低,另一种是基于链表的有界阻塞队列,底层是单向链表实现的,默认是无界的,也支持有界,当创建节点的时候进行添加数据,然后入队的时候会生成新的Node,它的底层有两把锁,分别在链表的头尾,链表的出队入队互不影响。

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

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

相关文章

Stable Diffusion 模型分享:A-Zovya RPG Artist Tools(RPG 大师工具箱)

本文收录于《AI绘画从入门到精通》专栏,专栏总目录:点这里。 文章目录 模型介绍生成案例案例一案例二案例三案例四案例五案例六案例七案例八 下载地址 模型介绍 A-Zovya RPG Artist Tools 模型是一个针对 RPG 训练的一个模型,可以生成一些 R…

基于springboot + vue实现的前后端分离-酒店管理系统

项目介绍 基于springboot vue实现的酒店管理系统一共有酒店管理员和用户这两种角色。 管理员功能 登录:管理员可以通过登录功能进入系统,确保只有授权人员可以访问系统。用户管理:管理员可以添加、编辑和删除酒店的用户,包括前…

最新YOLOv9论文理论:使用可编程梯度信息学习您想学习的内容 | Programmable Gradient Information

YOLOv9: Learning What You Want to Learn Using Programmable Gradient Information YOLOv9 论文地址:https://arxiv.org/pdf/2402.13616.pdf 摘要 当今的深度学习方法侧重于如何设计最合适的目标函数,以便模型的预测结果最接近真实情况。同时&…

精美工作室引导页源码

精美工作室引导页源码 源码介绍:一款精美的工作室引导页源码 下载地址: https://www.changyouzuhao.cn/6560.html

⭐北邮复试刷题LCR 018. 验证回文串__双指针 (力扣119经典题变种挑战)

LCR 018. 验证回文串 给定一个字符串 s ,验证 s 是否是 回文串 ,只考虑字母和数字字符,可以忽略字母的大小写。 本题中,将空字符串定义为有效的 回文串 。 示例 1: 输入: s “A man, a plan, a canal: Panama” 输出: true 解释…

正交匹配追踪(Orthogonal Matching Pursuit, OMP)的MATLAB实现

压缩感知(Compressed Sensing, CS)是一种利用稀疏信号的先验知识,用远少于奈奎斯特采样定理要求的样本数目恢复整个信号的技术。正交匹配追踪(Orthogonal Matching Pursuit, OMP)是一种常见的贪婪算法(Gree…

沁恒CH32V30X学习笔记02--GPIO的使用教程及2次封装驱动

gpio 概述 刚复位后,GPIO 口运行在初始状态,这时大多数 IO 口都是运行在浮空输入状态 外部中断 所有的 GPIO 口都可以被配置外部中断输入通道,但一个外部中断输入通道最多只能映射到一个 GPIO 引脚上,且外部中断通道的序号必须和 GPIO 端口的位号一致,比如 PA1(或 PB1、…

windows下快速安装nginx 并配置开机自启动

1、下载地址:http://nginx.org/en/download.html 2、启动nginx 注意⚠️ 不要直接双击nginx.exe,这样会导致修改配置后重启、停止nginx无效,需要手动关闭任务管理器内的所有nginx进程。 在nginx.exe目录,打开命令行工具&#xf…

Linux搭建FISCO BCOS的第一个区块链网络

一、前言 FISCO BCOS是由金融区块链合作联盟(深圳)与微众银行共同发起的开源区块链项目,支持多链多账本,满足金融行业复杂业务需求。本文将介绍如何在Ubuntu操作系统上使用Linux命令搭建FISCO BCOS的第一个区块链网络。 目录 一…

Jetpack Compose -> 重组作用域和remember()

前言 上一章我们讲解了 MutableState 和 mutableStateOf() 本章我们讲解下 remember 这个关键方法; ReCompose Scope(重组作用域) 我们先来看一段代码 当我们将 var name by mutableStateOf("老A")lifecycleScope.launch{}这两行代码放到 setContent 中…

核密度分析

一.算法介绍 核密度估计(Kernel Density Estimation)是一种用于估计数据分布的非参数统计方法。它可以用于多种目的和应用,包括: 数据可视化:核密度估计可以用来绘制平滑的密度曲线或热力图,从而直观地表…

使用transformer来训练自己的大模型实现自定义AI绘图软件的详细操作步骤

使用transformer来训练自己的大模型实现自定义AI绘图软件的详细操作步骤!下面的步骤是非常细致的,如果你有一台自己的GPU算力还算可以的服务器主机,想自己训练AI大模型。可以按照如下步骤开展操作。 要使用 Transformer 框架训练属于自己的大模型来完成 AI 绘图,需要经历以…

数字之美:探索人工智能绘画的奇妙世界

目录 引言AI绘画的定义与发展历程定义与发展历程AI绘画产品有哪些? AI绘画的应用领域设计与创意产业影视与游戏制作数字艺术与展览 AI绘画的基本原理与技术深度学习与神经网络生成对抗网络(GAN)风格迁移算法 AI绘画效果展示一只带着墨镜的小猫在高楼林立…

Vivado 2015.4安装记录

一、资源 安装破解包:Vivado2015.4,提取码:4eaw 二、安装 2.0 解压缩 2.0.1 解压缩Xilinx_Vivado_SDK_Win_2015.4_1118_2.tar.gz 2.0.2 解压缩Xilinx_Vivado_SDK_Win_2015.4_1118_2.tar 2.1 安装 安装驱动前,要拔掉与电脑连接的Xilinx下载…

qt-动画圆圈等待-LED数字

qt-动画圆圈等待-LED数字 一、演示效果二、关键程序三、下载链接 一、演示效果 二、关键程序 #include "LedNumber.h" #include <QLabel>LEDNumber::LEDNumber(QWidget *parent) : QWidget(parent) {//设置默认宽高比setScale((float)0.6);//设置默认背景色se…

fastjson解析自定义get方法导致空指针问题

背景 为了在日志中把出入参打印出来&#xff0c;以便验证链路和排查问题&#xff0c;在日志中将入参用fastjson格式化成字符串输出&#xff0c;结果遇到了NPE。 问题复现 示例代码 public static void main(String[] args) {OrganizationId orgId new OrganizationId();N…

HTML+CSS+JS:花瓣登录组件

效果演示 实现了一个具有动态花朵背景和简洁登录框的登录页面效果。 Code <section><img src"./img/background.jpeg" class"background"><div class"login"><h2>Sign In</h2><div class"inputBox"…

CoordConv(NeurIPS 2018)

paper&#xff1a;An Intriguing Failing of Convolutional Neural Networks and the CoordConv Solution official implementation&#xff1a;https://github.com/uber-research/coordconv 存在的问题 本文揭示并分析了CNN在两种不同类型空间表示之间转换能力的欠缺&#…

新书推荐:《分布式商业生态战略:未来数字商业新逻辑与企业数字化转型新策略》

近两年&#xff0c;商业经济环境的不确定性越来越明显&#xff0c;市场经济受到疫情、技术、政策等多方因素影响越来越难以预测&#xff0c;黑天鹅事件时有发生。在国内外经济方面&#xff0c;国际的地缘政治对商业经济产生着重大的影响&#xff0c;例如供应链中断&#xff0c;…

这才是No.1的门禁管理技巧!赶紧抄作业

随着社会的不断发展和科技的飞速进步&#xff0c;安全管理成为各个领域不可或缺的重要环节。在这个背景下&#xff0c;门禁监控系统作为一种先进而高效的安全管理工具逐渐受到了广泛关注和应用。 客户案例 企业大厦管理 在江苏某繁忙的商业大厦中&#xff0c;管理人员常常面临…