【Java EE】总结12种锁策略以及synchronized的实现原理

˃͈꒵˂͈꒱ write in front ꒰˃͈꒵˂͈꒱
ʕ̯•͡˔•̯᷅ʔ大家好,我是xiaoxie.希望你看完之后,有不足之处请多多谅解,让我们一起共同进步૮₍❀ᴗ͈ . ᴗ͈ აxiaoxieʕ̯•͡˔•̯᷅ʔ—CSDN博客
本文由xiaoxieʕ̯•͡˔•̯᷅ʔ 原创 CSDN 如需转载还请通知˶⍤⃝˶​
个人主页:xiaoxieʕ̯•͡˔•̯᷅ʔ—CSDN博客

系列专栏:xiaoxie的JAVAEE学习系列专栏——CSDN博客●'ᴗ'σσணღ
我的目标:"团团等我💪( ◡̀_◡́ ҂)" 

( ⸝⸝⸝›ᴥ‹⸝⸝⸝ )欢迎各位→点赞👍 + 收藏⭐️ + 留言📝​+关注(互三必回)!

目录

 一.锁策略

1.悲观锁 vs 乐观锁

1.悲观锁:

2.乐观锁:

3.总结

2.重量级锁 vs 轻量级锁 

1.重量级锁:

2.轻量级锁:

3.对比:

3.自旋锁 vs  挂起等待锁

1.自旋锁:

2.挂起等待锁:

3.总结

4.可重入锁 vs 不可重入锁

1.可重入锁:

2.不可重入锁:

3.总结

5.公平锁和非公平锁

1.公平锁 (Fair Lock):

2.非公平锁 (Non-Fair Lock):

 3.总结

6.互斥锁 vs 读写锁

1.互斥锁:

2.读写锁:

3.总结:

 二.synchronized的实现原理

 1.锁升级(synchronized的自适应)

1.锁升级过程

2.偏向锁(面试常考)

1.图书馆借书证办理的例子

2.总结

2.锁消除(编译器优化策略)

3.锁粗化 


 

 一.锁策略

1.悲观锁 vs 乐观锁

1.悲观锁

  • 悲观锁认为数据在并发访问时极有可能会发生冲突,所以在数据获取阶段就先加锁,只有获得锁的线程才能进行数据的读写操作。
  • 在数据库层面,悲观锁可以通过 SELECT ... FOR UPDATEUPDATE 或 DELETE 加上 WHERE 子句锁定符合条件的数据行。
  • 在事务结束后,数据库会自动释放锁。
  • 优点:能有效避免脏读、不可重复读、幻读等问题,适合并发更新较为频繁且数据一致性要求高的场景。
  • 缺点:过度依赖锁会导致并发性能下降,尤其是在锁竞争激烈时可能出现大量线程等待,造成资源浪费和响应延迟。

2.乐观锁

  • 乐观锁假定在大多数情况下,数据在并发访问时不会有冲突,只有在提交更新时才会检查是否发生了冲突。
  • 实现方式通常是在表中增加一个版本号字段(version)或其他标识符,读取数据时不加锁,但在更新时除了原条件外还会检查版本号是否发生变化。
  • 如在JPA或Hibernate中可通过@Version注解实现乐观锁,更新时如果版本号与之前读取时不一致,则表示在此期间已有其他事务修改了数据,此时事务回滚。
  • 优点:无锁状态下读取速度快,提高了并发性能,减少了锁的竞争开销。
  • 缺点:不适合并发更新特别频繁的场景,因为随着冲突概率的增加,会有更多的事务回滚;同时,频繁的版本号检测也会带来一定的性能损耗。

3.总结

总结来说,选择悲观锁还是乐观锁取决于具体的业务场景和并发程度,悲观锁适用于冲突概率较高、对数据一致性要求严格的场景,而乐观锁适用于冲突较少、追求高并发性能的场景。 

2.重量级锁 vs 轻量级锁 

1.重量级锁

  • 实现:依赖于操作系统的Mutex Lock(互斥锁)实现,因此状态转换涉及用户态内核态的切换,开销较大。
  • 优点:适用于线程竞争激烈且锁持有时间长的场景,可以有效避免CPU资源浪费。
  • 缺点:线程阻塞和唤醒涉及系统调用,可能导致性能问题,尤其是在高并发场景下。
  • 适用场景当存在多个线程长时间竞争同一把锁时,使用重量级锁较为合适。

2.轻量级锁

  • 实现:在JDK1.6中引入,通过CAS操作尝试获取锁,避免了操作系统层面的开销。
  • 优点:在没有多线程竞争或竞争不激烈的情况下,可以提高性能,因为避免了线程的阻塞和唤醒。
  • 缺点:如果线程始终无法获得锁,长时间自旋会消耗CPU资源。
  • 适用场景:适用于线程交替执行同步块,且同步块执行速度非常快的场景。

3.对比

  • 性能:轻量级锁在没有锁竞争的情况下性能更优,而重量级锁在高竞争环境下更合适。
  • 资源消耗:轻量级锁避免了操作系统资源的消耗,而重量级锁则涉及系统调用。
  • 锁升级:JVM中的锁机制设计为一个升级过程:无锁 -> 偏向锁 -> 轻量级锁 -> 重量级锁,目的是为了在不同的竞争情况下提供合适的同步机制。

3.自旋锁 vs  挂起等待锁

1.自旋锁

  • 自旋锁是一种积极的锁策略,当线程试图获取一个已被其他线程持有的锁时,不会立刻放弃处理器并进入睡眠状态,而是会不断循环(自旋)检查锁是否已被释放。
  • 这种做法的优点在于,如果锁持有者很快释放了锁,那么自旋线程能够立即获得锁,避免了线程上下文切换的开销,提升了响应速度。
  • 缺点在于,如果锁被长时间占用,自旋线程将持续占用CPU资源,导致性能浪费。因此,自旋锁适用于锁持有时间短且线程上下文切换开销较大的场景,如多核处理器环境下的短期同步

2.挂起等待锁

  • 挂起等待锁是一种消极的锁策略,当线程无法获取锁时,会选择主动让出CPU并进入等待队列(通常是被操作系统挂起),直到锁被释放并收到通知后才重新参与CPU调度
  • 这样做的优点是避免了无谓的CPU消耗,当锁被长时间占用时,自旋的线程会停止消耗CPU资源
  • 缺点是线程从等待状态恢复到运行状态需要经历上下文切换,这本身有一定的开销,尤其是频繁的上下文切换可能导致性能下降。
  • 挂起等待锁适用于锁持有时间不确定或可能较长的情况,因为它可以节省CPU资源,防止无效的循环等待

3.总结

在Java中,这两种锁策略的表现形式并不是直接作为API提供,而是JVM在实现锁机制时采取的策略,例如在自旋锁策略失效后,JVM会升级锁为重量级锁,这时线程就会进入挂起等待状态。在现代Java虚拟机中,锁机制往往是复合的,包括偏向锁、轻量级锁、重量级锁等不同层次的设计,其中轻量级锁在一定程度上借鉴了自旋锁的思想 

4.可重入锁 vs 不可重入锁

1.可重入锁

  1. 可重入锁是指同一个线程可以重复获取同一把锁,而不会发生死锁。这意味着如果一个线程已经持有了锁,当它再次请求同一把锁时,仍然可以得到许可并继续执行,而不是被阻塞。
  2. 在Java中,java.util.concurrent.locks.ReentrantLock 是一种典型的可重入锁实现,同时也支持公平锁和非公平锁的选择。
  3. 可重入锁内部会维护一个计数器,记录当前线程获取锁的次数,每获取一次锁计数器加1,每释放一次锁计数器减1,直到计数器为0时,锁真正释放。
  4. 可重入锁的优势在于支持递归调用和嵌套同步,简化了编程模型,并降低了死锁风险

2.不可重入锁

  1. 不可重入锁一旦被一个线程获取后,该线程如果再次请求该锁,就会被阻塞,无法获取,即一个线程不能再次获取自己已经持有的锁
  2. 不可重入锁不允许锁的持有线程对自己已经持有的锁进行二次锁定,因此在递归调用或者嵌套同步场景下容易产生死锁。

3.总结

总结起来,可重入锁相较于不可重入锁在处理多层同步和递归调用时具有更高的灵活性和安全性,不易引发死锁问题,但在某些特定场景下,不可重入锁也可能因其简洁性和性能特点而被选用。并且Java中的synchronized 就是可重入锁.

5.公平锁和非公平锁

1.公平锁 (Fair Lock)

  1. 公平锁是一种遵循先进先出(FIFO)原则的锁机制,当多个线程请求同一个锁时,会严格按照线程请求锁的顺序来决定哪个线程可以获得锁。也就是说,已经在等待队列中的线程会优先获得锁,不存在后来的线程抢占先来线程锁的情况。
  2. 公平锁的优势在于能够避免线程饥饿现象的发生,确保每个等待锁的线程最终都能够获得锁的机会,提高了线程调度的公平性
  3. 缺点是由于每次锁释放后都需要唤醒队列中等待最久的线程,这会增加线程上下文切换的开销,特别是在高并发场景下可能会降低系统的整体吞吐量。

2.非公平锁 (Non-Fair Lock)

  1. 非公平锁则不保证线程获取锁的顺序与请求锁的顺序一致,当锁可用时,任何等待的线程都有可能获取到锁,即使是刚刚开始等待的线程也可能优于已经在等待队列中的线程
  2. 非公平锁在没有等待队列或者锁刚释放时,允许线程“插队”,直接尝试获取锁,这种策略在某些场景下可以提高系统的整体性能,因为减少了线程上下文切换的次数。
  3. 缺点是可能导致部分线程长期得不到锁,形成线程饥饿,尤其是在连续的锁请求和释放中,总是有新来的线程抢先获得锁的情况下,这种情况尤为明显。

 3.总结

这里的公平是相对的,你也可以说,先来后到是公平的,或者是每个线程都有同等机会获取到锁是公平的,但是在Java中是这样定义公平的,在Java中,java.util.concurrent.locks.ReentrantLock类可以指定是否启用公平锁,通过构造函数传入true参数即可创建公平锁实例,否则默认为非公平锁Java的并发包中,公平锁强调的是线程获取锁的有序性和公平性,而非公平锁则倾向于提高系统的并发性能,但可能会使得部分线程在竞争锁时面临不公平待遇。开发者可以根据实际应用场景的需求来选择使用哪种类型的锁.

6.互斥锁 vs 读写锁

1.互斥锁

  1. 互斥锁是一种最基本的同步原语,它确保在同一时间只有一个线程能够访问被锁定的资源或临界区
  2. 当一个线程获取互斥锁后,其他所有试图获取相同锁的线程将被阻塞,直到拥有锁的线程释放锁为止。
  3. 在Java中,synchronized关键字以及java.util.concurrent.locks.ReentrantLock都可以实现互斥锁的功能。
  4. 互斥锁主要用于保护那些既需要进行读操作又需要进行写操作的共享资源,无论何种操作,都需要获取锁。

2.读写锁

  1. 读写锁是一种比互斥锁更细粒度的锁,它区分读操作和写操作,允许多个线程同时进行读操作,但同一时刻只允许一个线程进行写操作
  2. 当没有任何线程持有写锁时,多个读线程可以同时获取读锁并读取共享资源;一旦有线程请求写锁读线程将被阻塞,直到写锁被释放
  3. 在Java中,java.util.concurrent.locks.ReentrantReadWriteLock类是读写锁的典型实现。
  4. 读写锁适用于读操作远大于写操作的场景,可以大大提高并发读取数据的性能,减少锁竞争

3.总结

  • 互斥锁在任何时刻仅允许一个线程访问资源,无论读写
  • 读写锁允许多个线程同时读取资源,但在进行写操作时,禁止其他读写线程介入,确保数据一致性;
  • 使用读写锁可以显著提高读密集型场景下的并发性能,而互斥锁在所有情况下都能确保数据一致性,但在读多写少的情况下可能会导致性能瓶颈。

 二.synchronized的实现原理

synchronized悲观锁也是乐观锁,是重量锁,也是轻量级锁,重量级锁是自旋锁实现的,轻量级锁是挂起等待锁实现的,是可重入锁,非公平锁,以及互斥锁.

现在我们就从synchronized的实现原理来分析为什么它具有多种特性的锁机制

 1.锁升级(synchronized的自适应)

1.锁升级过程

2.偏向锁(面试常考)

synchronized中的锁升级机制中,偏向锁是一种针对无多线程竞争情况的优化。为了便于理解,我们可以用生活中的图书馆借书证办理过程来类比偏向锁的工作方式:

1.图书馆借书证办理的例子
  1. 新书证发放(偏向锁初始化): 假设你去图书馆办理借书证。在非繁忙时段,图书馆只有一个人在办理,工作人员会直接为你制作一张借书证,并将你的信息记录在证上,比如照片和姓名。这张借书证在之后的使用中会被认为“偏向”于你,因为你是第一个使用它的人。

  2. 借书(线程获得偏向锁): 当你再次来借书时,图书馆的系统会检查借书证,发现这张证已经被“偏向”于你,于是系统无需再次核对你的身份信息,直接允许你借书。这个过程非常快速,因为系统已经“认识”了你。

  3. 多人借书(锁竞争开始): 如果在某个时刻,另一个人也拿着这张借书证来借书,图书馆系统会意识到这张借书证不再“偏向”于单一的一个人,因此它需要采取更公平的方式来处理。此时,你的借书证会被“升级”,比如变成一张需要每次借书时都要核对信息的普通借书证。

  4. 借书证升级(偏向锁升级为轻量级锁或重量级锁): 根据图书馆的繁忙程度(即线程竞争的激烈程度),这张借书证可能只是简单地每次要求你出示身份证来确认身份(类似于轻量级锁),或者可能需要你排队等待,直到轮到你办理借书手续(类似于重量级锁)。

2.总结
  1. 初始无锁状态:当一个对象被创建时,它的锁对象(由对象头中的Mark Word表示)处于无锁状态。这意味着没有线程正在使用这个锁,也没有任何锁标记。

  2. 线程首次获取锁:当第一个线程尝试进入synchronized块时,如果之前没有线程获取过这个锁,JVM会给这个锁打上一个偏向标记,这个标记指向当前获取锁的线程。这个过程是非常快速的,因为实际上并没有进行真正的锁加锁操作,而只是在Mark Word中记录了线程的信息。

  3. 偏向锁:如果同一个线程再次尝试获取这个锁,JVM会检查Mark Word中的偏向标记。如果发现标记指向的是当前线程,那么JVM允许该线程直接获取锁,无需进行任何同步操作。这样就大大提高了线程获取锁的效率。

  4. 锁竞争出现:如果有另一个线程也尝试获取这个锁,JVM会检测到锁竞争。这时,偏向锁会被撤销,锁的状态会升级为轻量级锁轻量级锁使用CAS(Compare-And-Swap)操作来尝试获取锁,如果失败,线程会进行自旋等待

  5. 锁状态进一步升级:如果自旋等待仍然无法获取锁,或者有更多线程参与竞争,轻量级锁可能会进一步升级为重量级锁。重量级锁涉及到操作系统级别的锁机制,线程在获取锁失败后会被阻塞,直到锁被释放。

通过这个锁升级的过程,Java虚拟机在保证线程安全的同时,尽可能地优化了锁的性能,以适应不同的并发场景。偏向锁特别适合于那些在大部分时间里只有一个线程会访问的同步资源,这样可以避免不必要的锁开销,提高程序的执行效率。

注意:升级过程是单向的,该锁的升级过程是不可逆的也就是说,当锁升级成重量级锁时就不可能退化为轻量级锁了

2.锁消除(编译器优化策略)

锁消除是Java虚拟机(JVM)中的一个优化技术,它发生在即时编译(JIT)过程中。锁消除主要针对的是那些通过逃逸分析(Escape Analysis)确认为不会逃逸出方法的对象,即这些对象不会被其他线程所访问,因此可以被认为是线程安全的。

在多线程环境中,为了保证数据的一致性和线程安全,通常会使用同步代码块来避免并发问题。然而,在某些情况下,如果编译器能够证明某个共享资源的读写操作不存在数据竞争(即在并发执行时,不会有多个线程同时修改该资源),那么这些同步措施就是多余的。

锁消除的常见场景

  1. 栈上分配:如果一个对象的生命周期被限制在单个方法内部,并且不会逃逸到其他线程,那么这个对象上的操作可以认为是线程安全的,因此可以消除对它的锁保护。

  2. 单线程代码块:如果代码块被证明只会由一个线程执行,那么该代码块内的锁可以被消除。

  3. 方法内联:在某些情况下,当JVM对代码进行内联操作时,如果内联导致某些代码路径的执行变得不可能,那么这些路径上的锁可以被消除。

  4. 不变性变量:如果一个变量在初始化后其值不再发生变化,那么该变量是线程安全的,因为它不会被任何线程修改。

锁消除的过程

  1. 逃逸分析:JVM通过逃逸分析来确定对象的作用域,即对象是否会逃逸到其他线程。

  2. 确定锁的安全性:如果对象被证明不会逃逸,并且锁的保护范围仅限于这个对象,那么JVM可以认为这个锁是多余的。

  3. 编译器优化:在即时编译过程中,JVM的编译器会根据逃逸分析的结果,对代码进行优化,移除那些被认为是多余的锁。

锁消除的优点

  • 性能提升:消除不必要的锁操作可以减少同步开销,从而提高程序的执行效率。

  • 减少资源消耗:减少锁的使用可以降低操作系统资源的消耗,如减少对互斥量的申请。

锁消除是JVM优化的一部分,它依赖于逃逸分析和编译器的优化能力。在某些情况下,锁消除可以显著提升程序性能,但也要注意,过度优化可能会导致难以发现的并发问题,因此需要谨慎使用。

3.锁粗化 

 锁粗化是Java虚拟机(JVM)为了减少不必要的锁获取和释放操作而进行的一种优化手段。在某些情况下,连续的多个对同一对象锁的加锁和解锁操作可能会导致性能损失,因为每次加解锁都会涉及到线程状态的切换、内存屏障的插入等开销。为了提高性能,JVM会对这些连续的琐碎加锁操作进行合并,使之成为一个范围更大的锁。

1for (int i = 0; i < array.length; i++) {
2    synchronized (this) {
3        // 对数组元素进行操作
4    }
5}

在这种情况下,如果JVM检测到循环体内部的同步代码块频繁执行且涉及的对象锁是同一个,那么它可能会将锁粗化为:

1synchronized (this) {
2    for (int i = 0; i < array.length; i++) {
3        // 对数组元素进行操作
4    }
5}

通过锁粗化,可以减少系统因频繁申请和释放锁带来的性能消耗,同时也降低了死锁的可能性。但是,锁粗化的优化行为并不是总能被触发,需要满足一定的条件,例如编译器或JVM能够识别到一系列相邻的锁操作,并且这些操作都在一个很短的时间窗口内完成。

感谢你的阅读,祝你一天愉快

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C语言 | Leetcode C语言题解之第49题字母异位词分组

题目&#xff1a; 题解&#xff1a; /*1.将字符串原串与副本进行绑定成一个节点2.对字符串副本进行按ascii码表进行从小到大排序3.按照字符串进行比较排序4.合并 */ typedef struct Node{char*s;char*s_vice;int len; }Node;void sortShellChar(char*s,int len){for(int dista…

element-ui upload 组件 手动多次出发 submit

element 上传组件 upload 上传成功以后&#xff0c;想重新 调用 submit()函数&#xff0c;发现是不可以进行多次触发的,。 直接上解决方法&#xff0c;在上传成功后的钩子函数里添加:fileList[0l.status ready fileList是文件列表&#xff0c;status是单文件的状态改成ready就…

致力于为企业提升媒体宣传的一种新策略-软文发稿和投放

随着新媒体时代的快速发展&#xff0c;媒体宣发的方式也在不断迭代&#xff0c;其中&#xff0c;“软文发稿”成为了许多企业非常看重的一种媒体宣发方式。那么&#xff0c;什么是“软文发稿”呢&#xff1f;这是一种通过撰写有新闻属性的广告文章&#xff0c;将企业的品牌、产…

Oracle故障处理:ORA-00600错误处理思路

提前说明&#xff1a; 该故障&#xff0c;我只是旁观者。 但处理该故障的DBA工程师&#xff0c;思路很清晰&#xff0c;我非常受教&#xff01;在此也将经验分享。 目录 项目场景 问题分析 优化建议 项目场景 在某项目数据库运维群&#xff0c;有现场同事发了张报错截图如下…

密码学 | Schnorr 协议:零知识身份证明和数字签名

&#x1f955;原文&#xff1a; Schnorr 协议&#xff1a;零知识身份证明和数字签名 &#x1f955;写在前面&#xff1a; 本文属搬运博客&#xff0c;自己留存学习。文中的小写字母表示标量&#xff0c;大写字母表示椭圆曲线中的点。 1 Schnorr 简介 Schnorr 由德国数学家和密…

c++中的指针

一、指针的基本概念 指针的作用&#xff1a;可以通过指针间接访问内存 内存编号是从0开始记录的&#xff0c;一般采用16进制数字表示。可以利用指针变量保存地址。 二、指针变量的定义和使用 指针变量定义语法&#xff1a; 数据类型 * 变量名 #include<iostream> u…

电脑怎么压缩视频?3个角度6个方法教会你视频压缩~

电脑端压缩视频的方法有很多&#xff0c;比如使用专业的视频压缩软件&#xff0c;提供更多的功能和选项&#xff0c;可以根据用户的需求进行更精细的设置和调整。具有更高的处理能力和优化的算法&#xff0c;能够更快速地完成视频压缩任务&#xff1b;比如使用在线网站&#xf…

HCIP-Datacom-ARST必选题库_01_ACL【7道题】

一、单选 1.下面是一台路由器的部分配置,关于该配置描述正确的是&#xff1a; 源地址为1.1.1.1的数据包匹配第一条ACL语句rule 0,匹配规则为允许 源地址为1.1.1.3的数据包匹配第三条ACL语句rule 2,匹配规则为拒绝 源地址为1.1.1.4的数据包匹配第四条ACL语句rule 3,匹配规则为允…

AOC vs. DAC:哪个更适合您的网络需求?

在现代网络通信中&#xff0c;选择合适的连接线缆对于数据传输的稳定性和速度至关重要。两种常见的线缆类型是 AOC&#xff08;Active Optical Cable&#xff09; 和 DAC&#xff08;Direct Attach Cable&#xff09;。本文将详细介绍这两种线缆的特点、优势和适用场景&#xf…

想提高办公效率和质量的系统都有哪些?

我们这一波人是幸运的&#xff0c;从毕业后参加工作就开始接触到各种的办公软件&#xff0c;第一次让我觉得神奇且实用的就是office&#xff0c;可以根据场景进行不同的分类使用。 后来又有电子邮件、门户网站、聊天工具、财务软件、智能手机等不同的电子化工具陆续出现...而进…

实用的查询网站

1. 元器件网站 ALLDATASHEETCN.COM - 电子元件和半导体及其他半导体的数据表搜索网站。 热门电子元器件搜索 2. 聆思科技CSK6系芯片资料 CSK6 是聆思科技新一代的 AI 芯片 SoC 产品系列,采用多核异构架构,集成了 “星辰” ARM Star MCU、HiFi4 DSP以及聆思全新设计的 AI 神…

云原生架构(CloudNative)|文末送资料:马-云原生微服务治理大厂冲刺班56期

目录 文末福利&#xff1a;送资料 前言 一、部署架构发展史 二、三大技术基石 三、云原生的优点&#xff1a; 文末福利&#xff1a;送资料 云原生-马哥-云原生微服务治理大厂冲刺班56期[完结 第01节全新马哥Linux云计算高薪就业实战班VIP体验课 第02节ceph企业级存储实…

统一威胁情报如何赋能SOC应对复杂威胁?

安全运营中心&#xff08;SOC&#xff09;是组织网络安全战略的核心组成部分&#xff0c;扮演着至关重要的角色。其负责实时监控整个IT基础设施&#xff0c;以检测、响应和预防各类网络安全威胁。网络安全威胁日益复杂且多变的数字化时代&#xff0c;攻击平面泛化、基础设施复杂…

python爬虫-----深入了解 requests 库下篇(第二十六天)

&#x1f388;&#x1f388;作者主页&#xff1a; 喔的嘛呀&#x1f388;&#x1f388; &#x1f388;&#x1f388;所属专栏&#xff1a;python爬虫学习&#x1f388;&#x1f388; ✨✨谢谢大家捧场&#xff0c;祝屏幕前的小伙伴们每天都有好运相伴左右&#xff0c;一定要天天…

PFA容量瓶耐受强酸强碱进口特氟龙材质定容瓶

PFA容量瓶&#xff0c;也叫特氟龙容量瓶&#xff0c;是用于配制标准浓度溶液的实验室器皿&#xff0c;是有着细长颈、梨形肚的耐强腐蚀平底塑料瓶&#xff0c;颈上有标线&#xff0c;可直接配置标准溶液和准确稀释溶液以及制备样品溶液。 因其有着不易碎、材质纯净、化学稳定性…

【数据结构|C语言版】算法效率和复杂度分析

前言1. 算法效率2. 大O的渐进表示法3. 时间复杂度3.1 时间复杂度概念3.2 时间复杂度计算举例 4. 空间复杂度4.1 空间复杂度的概念4.2 空间复杂度计算举例 5. 常见复杂度对比结语 ↓ 个人主页&#xff1a;C_GUIQU 个人专栏&#xff1a;【数据结构&#xff08;C语言版&#xff09…

Linux开发板配置静态IP

1、查看网口信息&#xff0c;易知eth0无IP地址 ifconfig2、首先分配一个IP地址 sudo ifconfig eth0 192.168.5.8 up3、此时配置的IP地址只是临时的&#xff0c;当你reboot重启板子上电后&#xff0c;ip地址会消失&#xff0c;因此需要为板子配置静态ip&#xff0c;避免每次上…

13 JavaScript学习:运算符

JavaScript 运算符 JavaScript 中有多种类型的运算符&#xff0c;包括以下几类&#xff1a; 算术运算符&#xff1a;用于执行基本的数学运算&#xff0c;如加法&#xff08;&#xff09;、减法&#xff08;-&#xff09;、乘法&#xff08;*&#xff09;、除法&#xff08;/&a…

力扣刷题学习(跟随视频学着刷)

使用入门 视频链接 【手把手带你刷Leetcode力扣&#xff5c;各个击破数据结构和算法&#xff5c;大厂面试必备技能【已完结】-哔哩哔哩】 https://b23.tv/vIcRT61 时空复杂度 时间&#xff1a; 空间&#xff1a;主要有O(1)和O(n)两种&#xff0c;只用计算开辟的内存&#xff…

java垃圾回收机制

java垃圾回收机制 我们知道&#xff0c;Java会自动管理和释放内存&#xff0c;它不像C/C那样要求我们手动管理内存&#xff0c;JVM提供了一套全自动的内存管理机制&#xff0c;当一个Java对象不再用到时&#xff0c;JVM会自动将其进行回收并释放内存&#xff0c;那么对象所占内…