Synchronized与Java线程的关系
前言
Java多线程处理任务时,为了线程安全,通常会对共享资源进行加锁,拿到锁的线程才能进行访问共享资源。而加锁方式通过都是Synchronized锁或者Lock锁。
那么多线程在协同工作的时候,线程状态的变化都与锁对象有关系。
Synchronized锁
Java采用synchronized关键字、以互斥同步的方式的解决线程安全问题。一般Synchronized主要用于同步代码块、实例方法、静态方法。
一、使用方式 字节码分析
// 使用
public synchronized void test1(){
}
public void test2(){synchronized(new Test()){}
}
public static synchronized void test3(){
}//反编译
public synchronized void test1();descriptor: ()Vflags: ACC_PUBLIC, ACC_SYNCHRONIZED // herepublic void test2();descriptor: ()flags: ACC_PUBLICCode:stack=2, locals=3, args_size=10: new #2 // class com/easy/helloworld/Test3: dup4: invokespecial #3 // Method "<init>":()V7: dup8: astore_19: monitorenter // here10: aload_111: monitorexit // here12: goto 2015: astore_216: aload_117: monitorexit // here18: aload_219: athrow20: returnpublic static synchronized void test3();descriptor: ()Vflags: ACC_PUBLIC, ACC_STATIC, ACC_SYNCHRONIZED // here
由上可知,同步代码:通过moniterenter、moniterexit 关联到到一个monitor对象,进入时设置Owner为当前线程,计数+1、退出-1。除了正常出口的 monitorexit,还在异常处理代码里插入了 monitorexit。
而实例方法、静态方式是隐式调用moniterenter、moniterexit。
二、Moniterenter、Moniterexit
monitorenter和monitorexit这两个jvm指令,主要是基于 Mark Word
和Object monitor
来实现的。
在 JVM 中,对象在内存中分为三块区域:
-
对象头:由
Mark Word
和Klass Point
构成。-
Mark Word(标记字段):用于存储对象自身的运行时数据,例如存储对象的HashCode,分代年龄、锁标志位等信息,是synchronized实现轻量级锁和偏向锁的关键。 64位JVM的Mark Word组成如下:
-
Klass Point(类型指针):对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。
-
-
实例数据:这部分主要是存放类的数据信息,父类的信息。
-
字节对齐:为了内存的IO性能,JVM要求对象起始地址必须是8字节的整数倍。对于不对齐的对象,需要填充数据进行对齐。
在JDK 1.6之前,synchronized
只有传统的锁机制,直接关联到monitor
对象,存在性能上的瓶颈。在JDK 1.6后,为了提高锁的获取与释放效率,JVM引入了两种锁机制:偏向锁和轻量级锁。它们的引入是为了解决在没有多线程竞争或基本没有竞争的场景下因使用传统锁机制带来的性能开销问题。这几种锁的实现和转换正是依靠对象头中的Mark Word
。
Synchronized锁机制
1、偏向锁
1)引入偏向锁的初衷
在没有多线程竞争的情况下,尽量减少不必要的轻量级锁的执行。轻量级锁的获取及释放依赖多次CAS原子指令,而偏向锁只依赖一次CAS原子指令。
但在多线程竞争时,需要进行偏向锁撤销步骤,因此其撤销的开销必须小于节省下来的CAS开销,否则偏向锁并不能带来收益。JDK 1.6中默认开启偏向锁,可以通过-XX:-UseBiasedLocking来禁用偏向锁。在JDK15中,已经默认禁用偏向锁了。
2)关键字
prototype_header
:JVM中的每个类有一个类似mark word
的prototype_header
,用来标记该class的epoch
和偏向开关等信息。
匿名偏向状态
:锁对象mark word标志位为101,且存储的Thread ID
为空时的状态(即锁对象为偏向锁,且没有线程偏向于这个锁对象)。
Atomic::cmpxchg_ptr
:CAS函数。这个方法有三个参数,依次为exchange_value
、dest
、compare_value
。如果dest的值为compare_value
则更新为exchange_value
,并返回compare_value
。否则,不更新并返回实际原值
。
3)偏向锁流程
步骤 1
、从当前线程的栈中找到一个空闲的Lock Record
,并指向当前锁对象。
步骤 2
、获取对象的markOop数据mark,即对象头的Mark Word;
步骤 3
、判断锁对象的mark word
是否是偏向模式,即低3位是否为101。若不是,进入步骤4。若是,计算anticipated_bias_locking_value
,判断偏向状态:
步骤 3.1
、anticipated_bias_locking_value
若为0,代表**偏向的线程是当前线程,且mark word
的epoch等于class的epoch,**这种情况下直接执行同步代码块,什么都不用做。
步骤 3.2
、判断class的prototype_header
是否为非偏向模式。若为非偏向模式,CAS尝试将对象恢复为无锁状态。无论cas是否成功都会进入轻量级锁逻辑。
步骤 3.3
、**如果epoch偏向时间戳已过期,则需要重偏向。**利用CAS指令将锁对象的mark word
替换为一个偏向当前线程且epoch为类的epoch的新的mark word
。
步骤 3.4
、CAS将偏向线程改为当前线程后,如果当前是匿名偏向(即对象头中的bit field存储的Thread ID为空)且无并发冲突,则能修改成功
获取偏向锁,否则进入锁升级
的逻辑。
步骤 4
、走到一步会进行轻量级锁逻辑。构造一个无锁状态的mark word
,然后存储到Lock Record
。
设置为无锁状态的原因是:轻量级锁解锁时是将对象头的
mark word
cas替换为Lock Record
中的Displaced Mark Word
,所以设置为无锁状态。如果是锁重入,则将Lock Record
的Displaced Mark Word
设置为null,放到栈帧中,起到计数作用。
注意
只有匿名偏向的对象才能进入偏向锁模式。JVM启动时会延时初始化偏向锁,默认是4000ms。初始化后会将所有加载的Klass的prototype header修改为匿名偏向样式。当创建一个对象时,会通过Klass的prototype_header来初始化该对象的对象头。
简单的说,偏向锁初始化结束后,后续所有对象的对象头都为匿名偏向样式,在此之前创建的对象则为无锁状态。而对于无锁状态的锁对象,如果有竞争,会直接进入到轻量级锁。这也是为什么JVM启动前4秒对象会直接进入到轻量级锁的原因。
那么为什么要延迟初始化?JVM启动时必不可免会有大量sync的操作,而偏向锁并不是都有利。如果开启了偏向锁,会发生大量锁撤销和锁升级操作,大大降低JVM启动效率。
4)偏向锁的撤销
偏向锁的 撤销
(revoke)是一个很特殊的操作,为了执行撤销操作,需要等待全局安全点
,此时所有的工作线程都停止了执行。
偏向锁的撤销操作并不是将对象恢复到无锁可偏向的状态,而是在偏向锁的获取过程中,发现可能存在竞争时,直接将一个被偏向的对象升级到
被加了轻量级锁的状态。
场景说明:
锁已经偏向线程A,此时线程B尝试获取锁。这种情况下会走到Mark标记的分支。
如果需要撤销的是当前线程,只要遍历当前线程的栈就能拿到lock record,可以直接调用revoke_bias
,不需要等到safe point再撤销。**在调用Object#hashcode时,也会走到该分支将为偏向锁的锁对象直接恢复为无锁状态。**若不是当前线程,会被push到VM Thread中等到safe point
的时候再执行。
VMThread内部维护了一个VMOperationQueue类型的队列,用于保存内部提交的VM线程操作VM_operation。GC、偏向锁的撤销等操作都是在这里被执行。
撤销流程:
**
步骤 1
、查看偏向的线程是否存活,如果已经死亡,则直接撤销偏向锁。**JVM维护了一个集合存放所有存活的线程,通过遍历该集合判断某个线程是否存活。
步骤 2
、偏向的线程是否还在同步块中,如果不在,则撤销偏向锁。如果在同步块中,执行步骤3。 而是否在同步块的判断依据偏向锁的重入计数方式:在偏向锁的获取中,每次进入同步块的时候都会在栈中找到第一个可用(即栈中最高的)的
Lock Record
,将其obj字段指向锁对象。每次解锁的时候都会把最低的Lock Record
移除掉,所以可以通过遍历线程栈中的Lock Record
来判断是否还在同步块中。轻量级锁的重入也是基于Lock Record
的计数来判断。
步骤 3
、升级为轻量级锁。将偏向线程所有相关Lock Record
的Displaced Mark Word
设置为null,再将最高位的Lock Record
的Displaced Mark Word
设置为无锁状态,然后将对象头指向最高位的Lock Record
。这里没有用到CAS指令,因为是在safepoint
,可以直接升级成轻量级锁。
小结
当只有一个线程反复进入同步块时,偏向锁带来的性能开销基本可以忽略,但是当有其他线程尝试获得锁时,就需要等到safe point
时将偏向锁撤销为无锁状态或升级为轻量级/重量级锁。
因此,JVM中增加了一种批量重偏向/撤销的机制以减少锁撤销的开销,而mark word中的epoch也是在这里被大量应用。但无论怎么优化,偏向锁的撤销仍有一定不可避免的成本。如果业务场景存在大量多线程竞争,那偏向锁的存在不仅不能提高性能,而且会导致性能下降。
总结
如果当前锁已偏向其他线程||epoch值过期||class偏向模式关闭||获取偏向锁的过程中存在并发冲突,都会进入到锁膨胀InterpreterRuntime::monitorenter
方法, 在该方法中会进行偏向锁撤销和升级。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2dzNki4f-1692344710003)(https://tech.youzan.com/content/images/2021/07/—.svg)]
2)轻量级锁
目的:
在多线程交替执行同步块的情况下,尽量避免重量级锁使用的操作系统互斥量带来的开销。但是如果多个线程在同一时刻进入临界区,会导致轻量级锁膨胀升级重量级锁,所以轻量级锁的出现并非是要替代重量级锁。
在偏向锁逻辑中,cas失败会执行到InterpreterRuntime::monitorenter
。在轻量级锁逻辑中,如果当前线程不是轻量级锁的重入,也会执行到InterpreterRuntime::monitorenter
。
其内部函数fast_enter
的流程,主要逻辑为revoke_and_rebias
:如果当前是偏向模式且偏向的线程还在使用锁,会将锁的mark word
改为轻量级锁的状态,并将偏向的线程栈中的Lock Record
修改为轻量级锁对应的形式(此时Lock Record是无锁状态),且返回值不是BIAS_REVOKED_AND_REBIASED
,会继续执行slow_enter
。
slow_enter流程步骤
步骤 1
、markOop mark = obj->mark()
方法获取对象的markOop数据mark;
步骤 2
、mark->is_neutral()
方法判断mark是否为无锁状态,标识位001;
步骤 3
、如果mark处于无锁状态,把mark保存到BasicLock对象(Lock Record的属性)的displaced_header字段;
步骤 3.1
、通过CAS尝试将Mark Word更新为指向BasicLock对象的指针,如果更新成功,表示竞争到锁,则执行同步代码,否则执行步骤4;
步骤 4
、如果是重入,则设置Displaced Mark Word为null。
步骤 5
、到这说明有多个线程竞争轻量级锁,轻量级锁需要膨胀升级为重量级锁;
轻量级锁的释放
轻量级锁释放时需要将Displaced Mark Word
替换回对象头的mark word
中。如果CAS失败或者是重量级锁则进入到InterpreterRuntime::monitorexit
方法中。monitorexit
直接调用slow_exit
方法释放Lock Record
。
最后执行的是如果是fast_exit方法,且是轻量级锁,尝试cas替换mark word
。若解锁时有竞争,会调用inflate
方法进行重量级锁膨胀,升级到到重量级锁后再执行exit
方法。
3)重量级锁
重量级锁通过对象内部的监视器(monitor)实现,其依赖于底层操作系统的Mutex Lock
实现,需要额外的用户态到内核态切换的开销。由上文分析,slow_enter
获取轻量级锁未成功时,会在inflate
中完成锁膨胀。
而inflate
其中是一个for循环,主要是为了处理多线程同时调用inflate的情况。然后会根据锁对象的状态进行不同的处理:
1.已经是重量级状态,说明膨胀已经完成,返回并继续执行ObjectMonitor::enter方法。
2.如果是轻量级锁则需要进行膨胀操作。
3.如果是膨胀中状态,则进行忙等待。
4.如果是无锁状态则需要进行膨胀操作。
步骤过程
步骤 1
、调用omAlloc
获取一个可用的ObjectMonitor
对象。在omAlloc
方法中会先从线程私有的monitor
集合omFreeList
中分配对象,如果omFreeList
中已经没有monitor
对象,则从JVM全局的gFreeList
中分配一批monitor
到omFreeList
中;
步骤 2
、通过CAS尝试将Mark Word设置为markOopDesc:INFLATING,标识当前锁正在膨胀中。如果CAS失败,说明同一时刻其它线程已经将Mark Word设置为markOopDesc:INFLATING,当前线程进行自旋等待膨胀完成。
步骤 3
、如果CAS成功,设置monitor的各个字段:设置monitor
的header字段为displaced mark word
,owner字段为Lock Record
,obj字段为锁对象等;
步骤 4
、设置锁对象头的mark word
为重量级锁状态,指向第一步分配的monitor
对象;
monitor竞争
当锁膨胀inflate
执行完并返回对应的ObjectMonitor
时,并不表示该线程竞争到了锁,真正的锁竞争发生在ObjectMonitor::enter
方法中。
monitor等待
ObjectMonitor
竞争失败的线程,通过自旋执行ObjectMonitor::EnterI
方法等待锁的释放。
EnterI大致原理
一个ObjectMonitor
对象包括两个同步队列(_cxq
和_EntryList
) ,以及一个等待队列_WaitSet
。cxq、EntryList 、WaitSet都是由ObjectWaiter构成的链表结构。其中,_cxq
为单向链表,_EntryList
为双向链表。
当一个线程尝试获得重量级锁且没有竞争到时,该线程会被封装成一个ObjectWaiter
对象插入到cxq的队列的队首,然后调用park
函数挂起当前线程,进入BLOCKED状态。
当线程释放锁时,会根据唤醒策略,从cxq或EntryList中挑选一个线程unpark
唤醒。如果线程获得锁后调用Object#wait
方法,则会将线程加入到WaitSet中,进入WAITING或TIMED_WAITING状态。
当被Object#notify
唤醒后,会将线程从WaitSet移动到cxq或EntryList中去,进入BLOCKED状态。需要注意的是,当调用一个锁对象的wait
或notify
方法时,若当前锁的状态是偏向锁或轻量级锁则会先膨胀成重量级锁。
monitor释放
当某个持有锁的线程执行完同步代码块时,会进行锁的释放。在HotSpot中,通过退出monitor的方式实现锁的释放,并通知被阻塞的线程,具体实现位于ObjectMonitor::exit
方法中。
Java线程
1、线程的实现
1)内核线程实现
内核线程(Kernel-Level Thread,KLT):由内核来完成线程切换,内核通过调度器对线程进行调度,并负责将线程的任务映射到各个处理器上。 程序一般不会直接去使用内核线程,而是使用内核线程的一种高级接口——轻量级进程(Light Weight Process,LWP),也就是通常意义上的线程。
优点:每个LWP都是独立的调度单元。一个LWP被阻塞,不影响其他LWP。
缺点:基于KLT,耗资源。线程的创建、析构、同步都需要进行系统调用,频繁的用户态、内核态切换。
2)用户线程实现(User Thread,UT)
广义:非内核线程,都可认为是用户线程。(包括LWT,虽然LWT的大多操作都要映射到KLT)
狭义:完全建立在用户空间的线程库上,系统内核不能感知线程存在的实现。UT也只感知到掌管这些UT的进程P。
优点:用户线程的创建、同步、销毁和调度完全在用户态中完成,不需要内核的帮助。
缺点:线程的创建、销毁、切换和调度都是用户必须考虑到问题。“阻塞如何处理”、“多处理器系统中如何将线程映射到其他处理器上”这类问题解决起来将会异常困难。
3)混合模式。即存在用户线程,也存在轻量级进程。
用户线程的创建、切换、析构等操作依然廉价,可以支持大规模的用户线程并发,且可以使用内核线程提供的线程调度功能及处理器映射。
线程的实现依赖操作系统支持的线程模型。在主流的操作系统上,hotspot、classic、art等虚拟机默认是 1:1的线程模型。在Solaris平台上,hotspot支持1:1、N:M两种线程模型。
2、线程的转换
线程的状态,指的是Thread 类中threadStatus的值。
创建Thread 类的对象,才能谈其状态。这个时候,线程t就处于新建状态。但他还不是“线程”。**调用start()后,会执行一个native方法创建内核线程。 这时候才有一个真正的线程创建出来,并即刻开始运行。这个内核线程与线程t进行1:1的映射。**这时候t具备运行能力,进入RUNNABLE状态。 RUNNABLE可以细分为READY和RUNNING,两者的区别只是是否等待到了资源并开始运行。
处于RUNNABLE且未运行的线程,会进入一个就绪队列中,等待操作系统的调度。处于就绪队列的线程都在等待资源,这个资源可以是cpu的时间片、也可以是系统的IO。
3、线程相关方法
1)wait
通过object获得objectMonitor,将Thread封装成OjectWaiter对象,然后addWaiter
将它插入waitSet
中,进入waiting或timed_waiting状态。最后释放锁,并通过底层的park
方法挂起线程;
2)notify
通过object获得objectMonitor,调用objectMonitor的notify
方法。这个notify最后会走到ObjectMonitor::DequeueWaiter
方法,获取waitSet列表中的第一个ObjectWaiter节点。并根据不同的策略,将取出来的ObjectWaiter节点,加入到EntryList
或cxq
中。 notifyAll
的实现类似于notify
,主要差别在多了个for循环。
notify
和notifyAll
并不会立即释放所占有的ObjectMonitor对象,其真正释放ObjectMonitor的时间点是在执行monitorexit
指令。 一旦释放
ObjectMonitor
对象了,entryList
和cxq
中的ObjectWaiter节点会依据QMode
所配置的策略,通过ExitEpilog方法唤醒取出来的ObjectWaiter节点。被唤醒的线程,继续参与monitor的竞争。若竞争失败,重新进入BLOCKED状态
3)join
join
的本质仍然是 wait()
方法。在使用join
时,JVM会帮我们隐式调用notify
,因此我们不需要主动notify唤醒主线程。 而sleep()
方法最终是调用SleepEvent
对象的park方法
4)sleep
Thread.sleep
在jvm层面上是调用thread中SleepEvent
对象的park()
方法实现阻塞线程,在此过程中会通过判断时间戳来决定线程的睡眠时间是否达到了指定的毫秒。
5)park
park
、unpark
方法也与同步语义无关。每个线程都与一个许可(permit)关联。unpark
函数为线程提供permit,线程调用park
函数则等待并消耗permit。
小结
1、 JVM线程状态不代表内核线程状态。
2、BLOCKED的线程一定处于entryList或cxq中,而处于WAITING和TIMED WAITING的线程,可能是由于执行了sleep或park进入该状态,不一定在waitSet中。也就是说,处于BLOCKED状态的线程一定是与同步相关。由这可延伸出,调用 jdk 的 lock并获取不到锁的线程,进入的是 WAITING 或 TIMED_WAITING 状态,而不是BLOCKED状态。
总结
相关文章:
Synchronized与Java线程的关系
前言 Java多线程处理任务时,为了线程安全,通常会对共享资源进行加锁,拿到锁的线程才能进行访问共享资源。而加锁方式通过都是Synchronized锁或者Lock锁。 那么多线程在协同工作的时候,线程状态的变化都与锁对象有关系。 …...
使用本地电脑搭建可以远程访问的SFTP服务器
文章目录 1. 搭建SFTP服务器1.1 下载 freesshd 服务器软件1.3 启动SFTP服务1.4 添加用户1.5 保存所有配置 2. 安装SFTP客户端FileZilla测试2.1 配置一个本地SFTP站点2.2 内网连接测试成功 3. 使用cpolar内网穿透3.1 创建SFTP隧道3.2 查看在线隧道列表 4. 使用SFTP客户端&#x…...
批量修改文件名怎么操作?
批量修改文件名怎么操作?不管你使用电脑处理工作还是进行学习,都会在电脑中产生很多的文件,时间一久电脑里的文件更加杂乱无章,这时候如果不对电脑中的文件进行及时的管理,那么很可能出现文件丢失而你自己还发现不了的…...
【LeetCode】538.把二叉搜索树转换为累加树
题目 给出二叉 搜索 树的根节点,该树的节点值各不相同,请你将其转换为累加树(Greater Sum Tree),使每个节点 node 的新值等于原树中大于或等于 node.val 的值之和。 提醒一下,二叉搜索树满足下列约束条件…...
linux 安装 kibana
首先下载 kibana https://www.elastic.co/cn/downloads/kibana 然后上传到linux /usr/local 目录下解压安装 修改config/kibana.yml 配置文件,将elasticsearch.hosts 然后再nginx 中做一个端口映射,实现在浏览器中输入后xxxx:5602 nginx 可以将请求转发…...
STM32入门——IIC通讯
江科大STM32学习记录 I2C通信 I2C(Inter IC Bus)是由Philips公司开发的一种通用数据总线两根通信线:SCL(Serial Clock)、SDA(Serial Data)同步,半双工带数据应答支持总线挂载多设备…...
DTC 19服务学习2
紧跟上篇 0x04 reportDTCSnapshotRecordByDTCNumber 通过DTC和快照序列来获取DTC快照记录。 适用以下假设: — 服务器支持存储给定 DTC 的两个 DTCSnapshot 记录的能力。 — 此示例假定是上一个示例的延续。 — 假设服务器请求服务器存储的 DTC 编号 123456 的两个…...
【腾讯云 TDSQL-C Serverless 产品体验】基于腾讯云轻量服务器以及 TDSQL-C 搭建 LNMP WordPress 博客系统
文章目录 一、前言二、数据库发展与云原生数据库2.1 数据库发展简介2.2 云原生数据库简介2.2.1 云数据库与云原生数据库区别 三、腾讯云 TDSQL-C 数据库3.1 什么是腾讯云 TDSQL-C 数据库3.2 为什么推出 TDSQL-C 数据库?传统 MySQL 架构存在较多痛点3.2.1 传统 MySQL…...
【vue3】对axios进行封装,方便更改路由并且可以改成局域网ip访问(附代码)
对axios封装是在main.js里面进行封装,因为main.js是一个vue项目的入口 步骤: 在1处创建一个axios实例为http,baseURL是基础地址(根据自己的需求写),写了这个在vue界面调用后端接口时只用在post请求处写路由…...
Java IO流(三)线程模型
传统阻塞I/O模式 其中黄色框表示对象,蓝色框表示线程,白色框表示API方法 特点 采用阻塞IO模式获取输入数据每个连接都需要独立的线程完成数据的输入,业务处理和处理结果数据返回 潜在问题 并发数很大时,需要对应每个连接请求创建一个线程,所以占用资源很大连接创建后,若当前…...
string(模拟实现与深拷贝)
目录 深拷贝与浅拷贝 浅拷贝: 深拷贝 写时拷贝(了解) 模拟实现 准备 完整代码 深拷贝与浅拷贝 浅拷贝: 也称位拷贝,编译器只是将对象中的值拷贝过来。如果对象中管理资源,最后就会导致多个对象共享同一份资源,当一…...
5.Vue_Element
文章目录 1 Ajax1.1 Ajax介绍1.1.1 Ajax概述1.1.2 Ajax作用1.1.3 同步异步 1.2 Axios1.2.1 Axios的基本使用1.2.2 Axios请求方法的别名 2 前端工程化2.1 前端工程化特点2.2 Vue项目开发流程 3 Vue组件库Element3.1 Element介绍 1 Ajax 1.1 Ajax介绍 1.1.1 Ajax概述 Ajax: 全…...
链路追踪jaeger
这里的链路指的是客户端向服务发起一个请求,该请求所经过的路线,也可以说是该请求经过的流量 例如: 客户端发起一个下订单的请求其流量过程: request—>service—>order-web—>order_srv—>mysql—>order_srv—&…...
神经网络基础-神经网络补充概念-42-梯度检验
概念 梯度检验(Gradient Checking)是一种验证数值计算梯度与解析计算梯度之间是否一致的技术,通常用于确保实现的反向传播算法正确性。在深度学习中,通过梯度检验可以帮助验证你的神经网络模型是否正确地计算了梯度,从…...
<kernel>kernel 6.4 USB-之-hub_port_connect()分析
<kernel>kernel 6.4 USB-之-hub_port_connect()分析 kernel 6.4 USB系列文章如下: <kernel>kernel 6.4 USB-之-hub_event()分析 <kernel>kernel 6.4 USB-之-port_event()分析 <kern…...
linux驱动学习3-外部中断
在做中断试验时,发现中断驱动总是insmod失败,之后定位到 gpio_request 失败,之后是想到使用的野火做好的系统,在uEnv.txt中会加载大量设备树插件,将key相关的设备树插件屏蔽即可。 linux中断API函数 中断号 每个中断…...
vue中的canvas插件
vue中canvas插件有vue-konva、vue-fabricjs、vue-canvas-effect、vue-chartjs和vue-threejs等。详细介绍:1、vue-konva是一个用于在Vue.js中使用Konva.js的插件,Konva.js是一个功能强大的HTML5 2D 渲染引擎,可以用于创建交互式的Canvas应用程…...
分享图片 | 快速浏览网页资源,批量保存、一键分享图片
前言 小伙伴学习吉他,有时需要在互联网搜索曲谱资源,而多数曲谱均为图片,并且为多页,在电脑上显示练习很不方便,需要停下来点击鼠标进行翻页,影响练习的连贯性。 为了解决上述问题,通常把图片…...
Programming abstractions in C阅读笔记:p123-p126
《Programming Abstractions In C》学习第50天,p123-p126,总结如下: 一、技术总结 1.notaion 这也是一个在计算机相关书籍中出现的词,但有时却不是那么好理解,因为它可以指代很多对象,这里做一个记录。示…...
自然语言处理从入门到应用——LangChain:链(Chains)-[通用功能:LLMChain、RouterChain和SequentialChain]
分类目录:《自然语言处理从入门到应用》总目录 LLMChain LLMChain是查询LLM对象最流行的方式之一。它使用提供的输入键值(如果有的话,还包括内存键值)格式化提示模板,将格式化的字符串传递给LLM,并返回LLM…...
ElasticSearch-安装部署全过程
本文已收录于专栏 《中间件合集》 目录 概念说明什么是ElasticSearch什么是Kibana什么是RESTful API 提供服务安装过程安装ElasticSearch1.下载ElasticSearch 安装包2.解压安装包3.进入解压之后的文件夹4.创建一个data文件夹用来存储数据5.进入config文件夹编辑elasticsearch.y…...
mathematica报错:Tag Plus is \ Protected
在使用化简函数Simplify的时候使用了规则的语法,但是规则可能没有使用等号。 例如 Simplify[(1 - c^2)/d^2, c^2 d^2 1]等号被认为是赋值符号,要修改为两个等号: Simplify[(1 - c^2)/d^2, c^2 d^2 1]这样就不会报错了。...
Python Django 模型概述与应用
今天来为大家介绍 Django 框架的模型部分,模型是真实数据的简单明确的描述,它包含了储存的数据所必要的字段和行为,Django 遵循 DRY Principle 。它的目标是你只需要定义数据模型,然后其它的杂七杂八代码你都不用关心,…...
Golang Gorm 更新字段 save update updates
更新和删除操作的前提条件都是要在找到数据的情况下,先要查询到数据才可以做操作。 更新的前提的先查询到记录,Save保存所有字段,用于单个记录的全字段更新它会保控所有字段,即使零值也会保存。 在更新和删除之前,要利…...
springBoot 配置文件引入 redis 的相关参数说明
在Spring Boot应用中使用Redis作为缓存或数据存储时,可以在应用的配置文件中配置相关参数。下面是常用的Redis配置参数及其说明: spring.redis.host: Redis服务器主机地址,默认为localhost。spring.redis.port: Redis服务器端口,…...
Docker的使用心得:简化开发与部署的利器
开发与测试的无缝衔接: Docker让开发与测试之间的切换变得前所未有的顺畅。我可以在本地开发环境中创建一个与生产环境一致的Docker容器,这样不仅可以确保开发过程中不会出现意外问题,还可以在测试阶段避免不必要的繁琐配置。 跨平台的可移植…...
vue3 基于element plus对el-pagination进行二次封装
vue3 基于element plus对el-pagination进行二次封装 1、前言2、在components文件夹中新建pagination.vue文件3、在组件内使用分页 1、前言 在vue3项目中,如果每个列表页都敲一遍分页方法,显然是不合理的,那么,下面我将基于elemen…...
RuntimeError: result type Float can‘t be cast to the desired output type __int64报错解决方法
小白刚开始学习YOLOv5,跟随老哥的步骤走了一遍目标检测--手把手教你搭建自己的YOLOv5目标检测平台 最后训练最后一步出现RuntimeError: result type Float can‘t be cast to the desired output type __int64报错 解决方法:找到5.0版报错的loss.py中最…...
解析Python爬虫常见异常及处理方法
作为专业爬虫程序猿长期混迹于爬虫ip解决方案中,我们经常会遇到各种各样的异常情况。在爬虫开发过程中,处理这些异常是不可或缺的一部分。本文将为大家总结常见的Python爬虫异常,并分享相应的处理方法,帮助你避免绊倒在爬虫之路上…...
详解Spring的循环依赖问题、三级缓存解决方案源码分析
0、基础:Bean的生命周期 在Spring中,由于IOC的控制反转,创建对象不再是简单的new出来,而是交给Spring去创建,会经历一系列Bean的生命周期才创建出相应的对象。而循环依赖问题也是由Bean的生命周期过程导致的问题&#…...
wordpress 翻译主题/最近发生的新闻
友情提示,您阅读本篇博文的先决条件如下: 1、本文示例基于Microsoft SQL Server 2008 R2调测。 2、具备 Transact-SQL 编程经验和使用 SQL Server Management Studio 的经验。 3、熟悉或了解Microsoft SQL Server 2008中的空间数据类型。 4、具备相应&am…...
乌鲁木齐做网站有哪些公司/营销渠道策略
1.安装软件环境安装以下软件,并安装GeoServer imagemosaic拓展插件,用来连接并加载PostGIS数据库中的栅格数据。PostGIS 2.4,PostgreSQL 9.6,GeoServer 2.12,geoserver-2.12-SNAPSHOT-imagemosaic-jdbc-plugin2.导入栅…...
模板建房多少钱一平方/seo自然排名优化
我们在iOS调试中经常会看到Clang这个,那么Clang到底是什么呢?我们来简单了解一下。 Clang是一个C、C、OC语言的轻量级编译器。源代码发布于BSD协议下。Clang是由C编写,基于LLVM,发布于LLVM BSD许可证下的编译器。它与GNU C语言规范…...
做衣服类网站策划书/seo网页优化公司
TextView 是android基本的控件,用以显示一个静态文本框。 通常定义如下:<TextViewandroid:id"id/text_test"android:layout_width"wrap_content"android:layout_height"wrap_content"android:text"string/hello_…...
确定网站推广目标/优化大师有用吗
这个不是造轮子,只是整理轮子,我在搜索引擎里,遇到的思路都是第一种思路(至少按照标题那个关键词搜索是这样的)所以我想发出来。一是如果遇到大神帮忙发现了方案一的问题与弊病,那就更好啦。二是让大家不会像我一样,只…...
优秀企业网站的特点/网站怎么弄
可能是配置的问题。 我将hive.metastore.uris从配置文件中注释掉之后解决了hiveserver2启动成功但无法通过beeline连接的问题。 [rootnode03 conf]# vi hive-site.xml<property><name>hive.metastore.warehouse.dir</name><value>/user/hive_remote/wa…...