当前位置: 首页 > news >正文

Java并发编程面试题:锁(17题)

🧑 博主简介:CSDN博客专家历代文学网(PC端可以访问:https://literature.sinhy.com/#/?__c=1000,移动端可微信小程序搜索“历代文学”)总架构师,15年工作经验,精通Java编程高并发设计Springboot和微服务,熟悉LinuxESXI虚拟化以及云原生Docker和K8s,热衷于探索科技的边界,并将理论知识转化为实际应用。保持对新技术的好奇心,乐于分享所学,希望通过我的实践经历和见解,启发他人的创新思维。在这里,我希望能与志同道合的朋友交流探讨,共同进步,一起在技术的世界里不断学习成长。
技术合作请加本人wx(注明来自csdn):foreast_sea

在这里插入图片描述


在这里插入图片描述

Java并发编程面试题:锁(17题)

1. synchronized 用过吗?怎么使用?

在 Java 中,synchronized 是最常用的锁,它使用简单,并且可以保证线程安全,避免多线程并发访问时出现数据不一致的情况。

随着 JDK 版本的进化,synchronized 的性能也得到了进一步的提升,不再像以前样重量级了。

synchronized 可以用在方法和代码块中。

①、修饰方法

public synchronized void increment() {this.count++;
}

当在方法声明中使用了 synchronized 关键字,就表示该方法是同步的,也就是说,线程在执行这个方法的时候,其他线程不能同时执行,需要等待锁释放。

如果是静态方法的话,锁的是这个类的 Class 对象,因为静态方法是属于类级别的。

public static synchronized void increment() {count++;
}

②、修饰代码块

public void increment() {synchronized (this) {this.count++;}
}

同步代码块可以减少需要同步的代码量,颗粒度更低,更灵活。synchronized 后面的括号中指定了要锁定的对象,可以是 this,也可以是其他对象。

2. synchronized 的实现原理?

synchronized 是怎么加锁的呢?

synchronized 是 JVM 帮我们实现的,因此在使用的时候不用手动去 lock 和 unlock,JVM 会帮我们自动加锁和解锁。

①、synchronized 修饰代码块时,JVM 会通过 monitorentermonitorexit 两个指令来实现同步:

  • monitorenter 指向同步代码块的开始位置
  • monitorexit 指向同步代码块的结束位置。

使用 javap -c -s -v -l SynchronizedDemo.class 反编译一段 synchronized 代码块时,可以看到 monitorenter 和 monitorexit 指令。

三分恶面渣逆袭:monitorenter和monitorexit

②、synchronized 修饰方法时,JVM 会通过 ACC_SYNCHRONIZED 标记符来实现同步。

三分恶面渣逆袭:synchronized修饰同步方法

synchronized 锁住的是什么呢?

monitorenter、monitorexit 或者 ACC_SYNCHRONIZED 都是基于 Monitor 实现的。

实例对象结构里有对象头,对象头里面有一块结构叫 Mark Word,Mark Word 指针指向了monitor

所谓的 Monitor 其实是一种同步工具,也可以说是一种同步机制。在 Java 虚拟机(HotSpot)中,Monitor 是由ObjectMonitor 实现的,可以叫做内部锁,或者 Monitor 锁。

ObjectMonitor 的工作原理:

  • ObjectMonitor 有两个队列:_WaitSet、_EntryList,用来保存 ObjectWaiter 对象列表。
  • _owner,获取 Monitor 对象的线程进入 _owner 区时, _count + 1。如果线程调用了 wait() 方法,此时会释放 Monitor 对象, _owner 恢复为空, _count - 1。同时该等待线程进入 _WaitSet 中,等待被唤醒。
ObjectMonitor() {_header       = NULL;_count        = 0; // 记录线程获取锁的次数_waiters      = 0,_recursions   = 0;  //锁的重入次数_object       = NULL;_owner        = NULL;  // 指向持有ObjectMonitor对象的线程_WaitSet      = NULL;  // 处于wait状态的线程,会被加入到_WaitSet_WaitSetLock  = 0 ;_Responsible  = NULL ;_succ         = NULL ;_cxq          = NULL ;FreeNext      = NULL ;_EntryList    = NULL ;  // 处于等待锁block状态的线程,会被加入到该列表_SpinFreq     = 0 ;_SpinClock    = 0 ;OwnerIsThread = 0 ;}

可以类比一个去医院就诊的例子[18]:

  • 首先,患者在门诊大厅前台或自助挂号机进行挂号

  • 随后,挂号结束后患者找到对应的诊室就诊

    • 诊室每次只能有一个患者就诊;
    • 如果此时诊室空闲,直接进入就诊;
    • 如果此时诊室内有其它患者就诊,那么当前患者进入候诊室,等待叫号;
  • 就诊结束后,走出就诊室,候诊室的下一位候诊患者进入就诊室。

图片来源于网络:就诊

这个过程就和 Monitor 机制比较相似:

  • 门诊大厅:所有待进入的线程都必须先在入口 Entry Set挂号才有资格;
  • 就诊室:就诊室**_Owner**里里只能有一个线程就诊,就诊完线程就自行离开
  • 候诊室:就诊室繁忙时,进入等待区(Wait Set),就诊室空闲的时候就从**等待区(Wait Set)**叫新的线程

在这里插入图片描述

所以我们就知道了,同步是锁住的什么东西:

  • monitorenter,在判断拥有同步标识 ACC_SYNCHRONIZED 抢先进入此方法的线程会优先拥有 Monitor 的 owner ,此时计数器 +1。
  • monitorexit,当执行完退出后,计数器 -1,归 0 后被其他进入的线程获得。
会不会牵扯到 os 层面呢?

会,synchronized 升级为重量级锁时,依赖于操作系统的互斥量(mutex)来实现,mutex 用于保证任何给定时间内,只有一个线程可以执行某一段特定的代码段。

3. 除了原子性,synchronized 可见性,有序性,可重入性怎么实现?

synchronized 怎么保证可见性?
  • 线程加锁前,将清空工作内存中共享变量的值,从而使用共享变量时需要从主内存中重新读取最新的值。
  • 线程加锁后,其它线程无法获取主内存中的共享变量。
  • 线程解锁前,必须把共享变量的最新值刷新到主内存中。
synchronized 怎么保证有序性?

synchronized 同步的代码块,具有排他性,一次只能被一个线程拥有,所以 synchronized 保证同一时刻,代码是单线程执行的。

因为 as-if-serial 语义的存在,单线程的程序能保证最终结果是有序的,但是不保证不会指令重排。

所以 synchronized 保证的有序是执行结果的有序性,而不是防止指令重排的有序性。

synchronized 怎么实现可重入的呢?

可重入意味着同一个线程可以多次获得同一个锁,而不会被阻塞。具体来说,如果一个线程已经持有某个锁,那么它可以再次进入该锁保护的代码块或方法,而不会被阻塞。

synchronized 之所以支持可重入,是因为 Java 的对象头包含了一个 Mark Word,用于存储对象的状态,包括锁信息。

当一个线程获取对象锁时,JVM 会将该线程的 ID 写入 Mark Word,并将锁计数器设为 1。

如果一个线程尝试再次获取已经持有的锁,JVM 会检查 Mark Word 中的线程 ID。如果 ID 匹配,表示的是同一个线程,锁计数器递增。

当线程退出同步块时,锁计数器递减。如果计数器值为零,JVM 将锁标记为未持有状态,并清除线程 ID 信息。

4. 锁升级?synchronized 优化了解吗?

什么是锁升级?

锁升级是 Java 虚拟机中的一个优化机制,用于提高多线程环境下 synchronized 的并发性能。锁升级涉及从较轻的锁状态(如无锁或偏向锁)逐步升级到较重的锁状态(如轻量级锁和重量级锁),以适应不同程度的竞争情况。

Java 对象头里的 Mark Word 会记录锁的状态,一共有四种状态:

①、无锁状态,在这个状态下,没有线程试图获取锁。

②、偏向锁,当第一个线程访问同步块时,锁会进入偏向模式。Mark Word 会被设置为偏向模式,并且存储了获取它的线程 ID。

偏向锁的目的是消除同一线程的后续锁获取和释放的开销。如果同一线程再次请求锁,就无需再次同步。

③、当有多个线程竞争锁,但没有锁竞争的强烈迹象(即线程交替执行同步块)时,偏向锁会升级为轻量级锁。

线程尝试通过CAS 操作(Compare-And-Swap)将对象头的 Mark Word 替换为指向锁记录的指针。如果成功,当前线程获取轻量级锁;如果失败,说明有竞争。

④、重量级锁,当锁竞争激烈时,轻量级锁会膨胀为重量级锁。

重量级锁通过将对象头的 Mark Word 指向监视器(Monitor)对象来实现,该对象包含了锁的持有者、锁的等待队列等信息。

在这里插入图片描述

synchronized 做了哪些优化?

在 JDK1.6 之前,synchronized 是直接调用 ObjectMonitor 的 enter 和 exit 实现的,这种锁也被称为重量级锁。这也是为什么很多声音说不要用 synchronized 的原因,有点“谈虎色变”的感觉。

从 JDK 1.6 开始,HotSpot 对 Java 中的锁进行优化,如增加了适应性自旋、锁消除、锁粗化、轻量级锁和偏向锁等优化策略,极大提升了 synchronized 的性能。

①、偏向锁:当一个线程首次获得锁时,JVM 会将锁标记为偏向这个线程,将锁的标志位设置为偏向模式,并且在对象头中记录下该线程的 ID。

之后,当相同的线程再次请求这个锁时,就无需进行额外的同步。如果另一个线程尝试获取这个锁,偏向模式会被撤销,并且锁会升级为轻量级锁。

②、轻量级锁:多个线程在不同时段获取同一把锁,即不存在锁竞争的情况,也就没有线程阻塞。针对这种情况,JVM 采用轻量级锁来避免线程的阻塞与唤醒。

当一个线程尝试获取轻量级锁时,它会在自己的栈帧中创建一个锁记录(Lock Record),然后尝试使用 CAS 操作将对象头的 Mark Word 替换为指向锁记录的指针。

如果成功,该线程持有锁;如果失败,表示有其他线程竞争,锁会升级为重量级锁。

③、自旋锁:当线程尝试获取轻量级锁失败时,它会进行自旋,即循环检查锁是否可用,以避免立即进入阻塞状态。

自旋的次数不是固定的,而是根据之前在同一个锁上的自旋时间和锁的状态动态调整的。

④、锁粗化:如果 JVM 检测到一系列连续的锁操作实际上是在单一线程中完成的,则会将多个锁操作合并为一个更大范围的锁操作,这可以减少锁请求的次数。

锁粗化主要针对循环内连续加锁解锁的情况进行优化。

⑤、锁消除:JVM 的即时编译器(JIT)可以在运行时进行代码分析,如果发现某些锁操作不可能被多个线程同时访问,那么这些锁操作就会被完全消除。锁消除可以减少不必要的同步开销。

锁升级的过程是什么样的?

无锁–>偏向锁—> 轻量级锁---->重量级锁。

三分恶面渣逆袭:锁升级方向

稍微加点描述:

在这里插入图片描述

完整的升级过程:

三分恶面渣逆袭:synchronized 锁升级过程

详细解释一下:

①、从无锁到偏向锁:

当一个线程首次访问同步块时,如果此对象无锁状态且偏向锁未被禁用,JVM 会将该对象头的锁标记改为偏向锁状态,并记录下当前线程的 ID。此时,对象头中的 Mark Word 中存储了持有偏向锁的线程 ID。

如果另一个线程尝试获取这个已被偏向的锁,JVM 会检查当前持有偏向锁的线程是否活跃。如果持有偏向锁的线程不活跃,则可以将锁重偏向至新的线程;如果持有偏向锁的线程还活跃,则需要撤销偏向锁,升级为轻量级锁。

②、偏向锁的轻量级锁:

进行偏向锁撤销时,会遍历堆栈的所有锁记录,暂停拥有偏向锁的线程,并检查锁对象。如果这个过程中发现有其他线程试图获取这个锁,JVM 会撤销偏向锁,并将锁升级为轻量级锁。

当有两个或以上线程竞争同一个偏向锁时,偏向锁模式不再有效,此时偏向锁会被撤销,对象的锁状态会升级为轻量级锁。

③、轻量级锁到重量级锁:

轻量级锁通过线程自旋来等待锁释放。如果自旋超过预定次数(自旋次数是可调的,并且自适应的),表明锁竞争激烈,轻量级锁的自旋已经不再高效。

当自旋等待失败,或者有线程在等待队列中等待相同的轻量级锁时,轻量级锁会升级为重量级锁。在这种情况下,JVM 会在操作系统层面创建一个互斥锁(Mutex),所有进一步尝试获取该锁的线程将会被阻塞,直到锁被释放。

5. synchronized 和 ReentrantLock 的区别?

synchronized是一个关键字,ReentrantLock是 Lock 接口的一个实现。

在这里插入图片描述

它们都可以用来实现同步,但也有一些区别:

  • ReentrantLock 可以实现多路选择通知(绑定多个 Condition),而 synchronized 只能通过 wait 和 notify/notifyAll 方法唤醒一个线程或者唤醒全部线程(单路通知);
  • ReentrantLock 必须手动释放锁。通常需要在 finally 块中调用 unlock 方法以确保锁被正确释放;synchronized 会自动释放锁,当同步块执行完毕时,由 JVM 自动释放,不需要手动操作。
  • ReentrantLock 通常能提供更好的性能,因为它可以更细粒度控制锁;synchronized 只能同步代码快或者方法,随着 JDK 版本的升级,两者之间性能差距已经不大了。
使用方式有什么不同?

synchronized 可以直接在方法上加锁,也可以在代码块上加锁(无需手动释放锁,锁会自动释放),而 ReentrantLock 必须手动声明来加锁和释放锁。

// synchronized 修饰方法
public synchronized void method() {// 业务代码
}// synchronized 修饰代码块
synchronized (this) {// 业务代码
}// ReentrantLock 加锁
ReentrantLock lock = new ReentrantLock();
lock.lock();
try {// 业务代码
} finally {lock.unlock();
}

随着 JDK 版本的升级,synchronized 的性能已经可以媲美 ReentrantLock 了,加入了偏向锁、轻量级锁和重量级锁的自适应优化等,所以可以大胆地用。

功能特点有什么不同?

如果需要更细粒度的控制(如可中断的锁操作、尝试非阻塞获取锁、超时获取锁或者使用公平锁等),可以使用 Lock。

  • ReentrantLock 提供了一种能够中断等待锁的线程的机制,通过 lock.lockInterruptibly()来实现这个机制。
  • ReentrantLock 可以指定是公平锁还是非公平锁。
  • ReentrantReadWriteLock 读写锁,读锁是共享锁,写锁是独占锁,读锁可以同时被多个线程持有,写锁只能被一个线程持有。这种锁的设计可以提高性能,特别是在读操作的数量远远超过写操作的情况下。

Lock 还提供了newCondition()方法来创建等待通知条件Condition,比 synchronized 与 wait()notify()/notifyAll()方法的组合更强大。

ReentrantLock lock = new ReentrantLock();
Condition condition = lock.newCondition();
并发量大的情况下,使用 synchronized 还是 ReentrantLock?

在并发量特别高的情况下,ReentrantLock 的性能可能会优于 synchronized,原因包括:

  • ReentrantLock 提供了超时和公平锁等特性,可以更好地应对复杂的并发场景 。
  • ReentrantLock 允许更细粒度的锁控制,可以有效减少锁竞争。
  • ReentrantLock 支持条件变量 Condition,可以实现比 synchronized 更复杂的线程间通信机制。
Lock 了解吗?

Lock 是 Java java.util.concurrent.locks 包中的一个接口,最常用的实现类是 ReentrantLock,提供了比 synchronized 更多的功能,如可中断的锁操作、尝试非阻塞获取锁、超时获取锁或者使用公平锁等。

Lock.lock()的具体实现逻辑了解吗?

lock 方法用来获取锁,如果锁已经被其他线程获取,当前线程会一直等待直到获取锁。

具体实现由内部的 Sync 类来实现,ReentrantLock 有两种 Sync 实现:NonfairSyncFairSync,分别对应非公平锁和公平锁。

二哥的Java 进阶之路:Lock.lock() 方法源码

非公平锁尝试让当前线程直接通过 CAS 操作获取锁,如果获取失败则进入 AQS 队列等待。

final void lock() {if (compareAndSetState(0, 1))  // 尝试直接获取锁setExclusiveOwnerThread(Thread.currentThread());elseacquire(1);  // 如果获取失败,进入AQS队列等待
}

公平锁会直接让线程进入等待队列,按顺序获取锁,不允许插队。

final void lock() {acquire(1);
}

acquire 方法由 AQS(AbstractQueuedSynchronizer)提供,是整个锁机制的核心,管理着获取和释放锁的流程、队列等待、线程调度等复杂操作。

public final void acquire(int arg) {if (!tryAcquire(arg) &&acquireQueued(addWaiter(Node.EXCLUSIVE), arg))selfInterrupt();
}

6. AQS 了解多少?

AQS,也就是抽象队列同步器,由 Doug Lea 设计,是 Java 并发包java.util.concurrent的核心框架类,许多同步类的实现都依赖于它,如 ReentrantLockSemaphoreCountDownLatch 等。

AQS 的思想是,如果被请求的共享资源空闲,则当前线程能够成功获取资源;否则,它将进入一个等待队列,当有其他线程释放资源时,系统会挑选等待队列中的一个线程,赋予其资源。

整个过程通过维护一个 int 类型的状态和一个先进先出(FIFO)的队列,来实现对共享资源的管理。

AQS抽象队列同步器

①、同步状态 state 由 volatile 修饰,保证了多线程之间的可见性;

private volatile int state;

②、同步队列是通过内部定义的 Node 类来实现的,每个 Node 包含了等待状态、前后节点、线程的引用等。

static final class Node {static final int CANCELLED =  1;static final int SIGNAL    = -1;static final int CONDITION = -2;static final int PROPAGATE = -3;volatile Node prev;volatile Node next;volatile Thread thread;
}

AQS 支持两种同步方式:

  • 独占模式:这种方式下,每次只能有一个线程持有锁,例如 ReentrantLock。
  • 共享模式:这种方式下,多个线程可以同时获取锁,例如 Semaphore 和 CountDownLatch。

子类可以通过继承 AQS 并实现它的方法来管理同步状态,这些方法包括:

  • tryAcquire:独占方式尝试获取资源,成功则返回 true,失败则返回 false;
  • tryRelease:独占方式尝试释放资源;
  • tryAcquireShared(int arg):共享方式尝试获取资源;
  • tryReleaseShared(int arg):共享方式尝试释放资源;
  • isHeldExclusively():该线程是否正在独占资源。

如果共享资源被占用,需要一种特定的阻塞等待唤醒机制来保证锁的分配,AQS 会将竞争共享资源失败的线程添加到一个 CLH 队列中。

三分恶面渣逆袭:CLH队列

在 CLH 锁中,当一个线程尝试获取锁并失败时,它会将自己添加到队列的尾部并自旋,等待前一个节点的线程释放锁。

三分恶面渣逆袭:AQS变种CLH队列

7. ReentrantLock 实现原理?

Lock 接口提供了比 synchronized 关键字更灵活的锁操作。ReentrantLock 就是 Lock 接口的一个实现,它提供了与 synchronized 关键字类似的锁功能,但更加灵活。

class CounterWithLock {private int count = 0;private final Lock lock = new ReentrantLock();public void increment() {lock.lock();  // 获取锁try {count++;} finally {lock.unlock();  // 释放锁}}public int getCount() {return count;}
}

increment 方法先上锁,然后尝试增加 count 的值,在完成操作后释放锁。这样就可以保证 count 的操作是线程安全的。

ReentrantLock 是可重入的独占锁,只能有一个线程获取该锁,其它想获取该锁的线程会被阻塞。

可重入表示当前线程获取该锁后再次获取不会被阻塞,也就意味着同一个线程可以多次获得同一个锁而不会发生死锁。

new ReentrantLock() 默认创建的是非公平锁 NonfairSync。在非公平锁模式下,锁可能会授予刚刚请求它的线程,而不考虑等待时间。

ReentrantLock 也支持公平锁,该模式下,锁会授予等待时间最长的线程。

ReentrantLock 内部通过一个计数器来跟踪锁的持有次数。当线程调用lock()方法获取锁时,ReentrantLock 会检查当前状态,判断锁是否已经被其他线程持有。如果没有被持有,则当前线程将获得锁;如果锁已被其他线程持有,则当前线程将根据锁的公平性策略,可能会被加入到等待队列中。

线程首次获取锁时,计数器值变为 1;如果同一线程再次获取锁,计数器增加;每释放一次锁,计数器减 1。

当线程调用unlock()方法时,ReentrantLock 会将持有锁的计数减 1,如果计数到达 0,则释放锁,并唤醒等待队列中的线程来竞争锁。

在这里插入图片描述

8. ReentrantLock 怎么实现公平锁的?

ReentrantLock 的默认构造方法创建的是非公平锁 NonfairSync。

public ReentrantLock() {sync = new NonfairSync();
}

可以通过有参构造方法传递 true 参数来创建公平锁 FairSync。

ReentrantLock lock = new ReentrantLock(true);
--- ReentrantLock
// true 代表公平锁,false 代表非公平锁
public ReentrantLock(boolean fair) {sync = fair ? new FairSync() : new NonfairSync();
}

FairSync、NonfairSync 都是 ReentrantLock 的内部类,分别实现了公平锁和非公平锁的逻辑。

非公平锁和公平锁有什么不同?

①、公平锁意味着在多个线程竞争锁时,获取锁的顺序与线程请求锁的顺序相同,即先来先服务(FIFO)。

虽然能保证锁的顺序,但实现起来比较复杂,因为需要额外维护一个有序队列。

二哥的 Java 进阶之路

②、非公平锁不保证线程获取锁的顺序,当锁被释放时,任何请求锁的线程都有机会获取锁,而不是按照请求的顺序。

怎么实现一个非公平锁呢?

要实现一个非公平锁,只需要在创建 ReentrantLock 实例时,不传递任何参数或者传递 false 给它的构造方法就好了。

9. CAS 了解多少?

推荐阅读:一文彻底搞清楚 Java 实现 CAS 的原理

在 Java 中,我们可以使用 synchronized)关键字和 CAS 来实现加锁效果。

CAS 是一种乐观锁的实现方式,全称为“比较并交换”(Compare-and-Swap),是一种无锁的原子操作。

synchronized 是悲观锁,尽管随着 JDK 版本的升级,synchronized 关键字已经“轻量级”了很多,但依然是悲观锁,线程开始执行第一步就要获取锁,一旦获得锁,其他的线程进入后就会阻塞并等待锁。

CAS 是乐观锁,线程执行的时候不会加锁,它会假设此时没有冲突,然后完成某项操作;如果因为冲突失败了就重试,直到成功为止。

CAS 原子性

在 CAS 中,有这样三个值:

  • V:要更新的变量(var)
  • E:预期值(expected)
  • N:新值(new)

比较并交换的过程如下:

判断 V 是否等于 E,如果等于,将 V 的值设置为 N;如果不等,说明已经有其它线程更新了 V,于是当前线程放弃更新,什么都不做。

这里的预期值 E 本质上指的是“旧值”。

这个比较和替换的操作是原子的,即不可中断,确保了数据的一致性。

举个例子,变量当前的值为 0,需要将其更新为 1,可以借助 AtomicInteger 类的 compareAndSet 方法来实现。

AtomicInteger atomicInteger = new AtomicInteger(0);
int expect = 0;
int update = 1;
atomicInteger.compareAndSet(expect, update);

compareAndSet 就是一个 CAS 方法,它调用的是 Unsafe 的 compareAndSwapInt。

为了保证CAS的原子性,CPU 提供了两种实现方式:

①、总线锁定,通过锁定 CPU 的总线,禁止其他 CPU 或设备访问内存。在进行操作时,CPU 发出一个 LOCK 信号,这会阻止其他处理器对内存地址进行操作,直到当前指令执行完成。

总线锁定:博客园的紫薇哥哥

②、缓存锁定,当多个 CPU 操作同一块内存地址时,如果该内存地址已经被缓存到某个 CPU 的缓存中,缓存锁定机制会锁定该缓存行,防止其他 CPU 对这块内存进行修改。

现代CPU基本都支持和使用缓存锁定机制。

10. CAS 有什么问题?如何解决?

CAS 存在三个经典问题。

在这里插入图片描述

什么是 ABA 问题?如何解决?

如果一个位置的值原来是 A,后来被改为 B,再后来又被改回 A,那么进行 CAS 操作的线程将无法知晓该位置的值在此期间已经被修改过。

可以使用版本号/时间戳的方式来解决 ABA 问题。

比如说,每次变量更新时,不仅更新变量的值,还更新一个版本号。CAS 操作时不仅要求值匹配,还要求版本号匹配。

public class OptimisticLockExample {private int version;private int value;public synchronized boolean updateValue(int newValue, int currentVersion) {if (this.version == currentVersion) {this.value = newValue;this.version++;return true;}return false;}
}

Java 的 AtomicStampedReference 类就实现了这种机制,它会同时检查引用值和 stamp 是否都相等。

二哥的 Java 进阶之路:AtomicStampedReference

循环性能开销

自旋 CAS,如果一直循环执行,一直不成功,会给 CPU 带来非常大的执行开销。

怎么解决循环性能开销问题?

在 Java 中,很多使用自旋 CAS 的地方,会有一个自旋次数的限制,超过一定次数,就停止自旋。

只能保证一个变量的原子操作

CAS 保证的是对一个变量执行操作的原子性,如果对多个变量操作时,CAS 目前无法直接保证操作的原子性的。

怎么解决只能保证一个变量的原子操作问题?

  • 可以考虑改用锁来保证操作的原子性
  • 可以考虑合并多个变量,将多个变量封装成一个对象,通过 AtomicReference 来保证原子性。

11. Java 有哪些保证原子性的方法?如何保证多线程下 i++ 结果正确?

在这里插入图片描述

  • 使用循环原子类,例如 AtomicInteger,实现 i++原子操作
  • 使用 juc 包下的锁,如 ReentrantLock ,对 i++操作加锁 lock.lock()来实现原子性
  • 使用 synchronized,对 i++操作加锁

12. 原子操作类了解多少?

当程序更新一个变量时,如果多线程同时更新这个变量,可能得到期望之外的值,比如变量 i=1,A 线程更新 i+1,B 线程也更新 i+1,经过两个线程操作之后可能 i 不等于 3,而是等于 2。因为 A 和 B 线程在更新变量 i 的时候拿到的 i 都是 1,这就是线程不安全的更新操作,一般我们会使用 synchronized 来解决这个问题,synchronized 会保证多线程不会同时更新变量 i。

其实除此之外,还有更轻量级的选择,Java 从 JDK 1.5 开始提供了 java.util.concurrent.atomic 包,这个包中的原子操作类提供了一种用法简单、性能高效、线程安全地更新一个变量的方式。

因为变量的类型有很多种,所以在 Atomic 包里一共提供了 13 个类,属于 4 种类型的原子更新方式,分别是原子更新基本类型、原子更新数组、原子更新引用和原子更新属性(字段)。

在这里插入图片描述

Atomic 包里的类基本都是使用 Unsafe 实现的包装类。

使用原子的方式更新基本类型,Atomic 包提供了以下 3 个类:

  • AtomicBoolean:原子更新布尔类型。

  • AtomicInteger:原子更新整型。

  • AtomicLong:原子更新长整型。

通过原子的方式更新数组里的某个元素,Atomic 包提供了以下 4 个类:

  • AtomicIntegerArray:原子更新整型数组里的元素。

  • AtomicLongArray:原子更新长整型数组里的元素。

  • AtomicReferenceArray:原子更新引用类型数组里的元素。

  • AtomicIntegerArray 类主要是提供原子的方式更新数组里的整型

原子更新基本类型的 AtomicInteger,只能更新一个变量,如果要原子更新多个变量,就需要使用这个原子更新引用类型提供的类。Atomic 包提供了以下 3 个类:

  • AtomicReference:原子更新引用类型。

  • AtomicReferenceFieldUpdater:原子更新引用类型里的字段。

  • AtomicMarkableReference:原子更新带有标记位的引用类型。可以原子更新一个布尔类型的标记位和引用类型。构造方法是 AtomicMarkableReference(V initialRef,boolean initialMark)。

如果需原子地更新某个类里的某个字段时,就需要使用原子更新字段类,Atomic 包提供了以下 3 个类进行原子字段更新:

  • AtomicIntegerFieldUpdater:原子更新整型的字段的更新器。
  • AtomicLongFieldUpdater:原子更新长整型字段的更新器。
  • AtomicStampedReference:原子更新带有版本号的引用类型。该类将整数值与引用关联起来,可用于原子的更新数据和数据的版本号,可以解决使用 CAS 进行原子更新时可能出现的 ABA 问题。

13. AtomicInteger 的原理?

一句话概括:使用 CAS 实现

以 AtomicInteger 的添加方法为例:

    public final int getAndIncrement() {return unsafe.getAndAddInt(this, valueOffset, 1);}

通过Unsafe类的实例来进行添加操作,来看看具体的 CAS 操作:

public final int getAndAddInt(Object var1, long var2, int var4) {int var5;do {var5 = this.getIntVolatile(var1, var2);} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));return var5;
}

compareAndSwapInt 是一个 native 方法,基于 CAS 来操作 int 类型变量。其它的原子操作类基本都是大同小异。

14. 线程死锁了解吗?该如何避免?

死锁发生在多个线程相互等待对方释放锁资源,导致所有线程都无法继续执行。

三分恶面渣逆袭:死锁示意图

那么为什么会产生死锁呢?

讲个笑话,死锁的产生也不是你想产生就产生的,它是有条件的:

在这里插入图片描述

  • 互斥条件:资源不能被多个线程共享,一次只能由一个线程使用。如果一个线程已经占用了一个资源,其他请求该资源的线程必须等待,直到资源被释放。
  • 持有并等待条件:一个线程至少已经持有至少一个资源,且正在等待获取额外的资源,这些额外的资源被其他线程占有。
  • 不可剥夺条件:资源不能被强制从一个线程中抢占过来,只能由持有资源的线程主动释放。
  • 循环等待条件:存在一种线程资源的循环链,每个线程至少持有一个其他线程所需要的资源,然后又等待下一个线程所占有的资源。这形成了一个循环等待的环路。
该如何避免死锁呢?

理解产生死锁的这四个必要条件后,就可以采取相应的措施来避免死锁,换句话说,就是至少破坏死锁发生的一个条件

  • 破坏互斥条件:这通常不可行,因为加锁就是为了互斥。
  • 破坏持有并等待条件:一种方法是要求线程在开始执行前一次性地申请所有需要的资源。
  • 破坏非抢占条件:占用部分资源的线程进一步申请其他资源时,如果申请不到,可以主动释放它占有的资源。
  • 破坏循环等待条件:对所有资源类型进行排序,强制每个线程按顺序申请资源,这样可以避免循环等待的发生。

15. 那死锁问题怎么排查呢?

首先从系统级别上排查,比如说在 Linux 生产环境中,可以先使用 top ps 等命令查看进程状态,看看是否有进程占用了过多的资源。

接着,使用 JDK 自带的一些性能监控工具进行排查,比如说 jps、jstat、jinfo、jmap、jstack、jcmd 等等。

比如说,使用 jps -l 查看当前 Java 进程,然后使用 jstack 进程号 查看当前 Java 进程的线程堆栈信息,看看是否有线程在等待锁资源。

来编写一个死锁程序:

class DeadLockDemo {private static final Object lock1 = new Object();private static final Object lock2 = new Object();public static void main(String[] args) {new Thread(() -> {synchronized (lock1) {System.out.println("线程1获取到了锁1");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}synchronized (lock2) {System.out.println("线程1获取到了锁2");}}}).start();new Thread(() -> {synchronized (lock2) {System.out.println("线程2获取到了锁2");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}synchronized (lock1) {System.out.println("线程2获取到了锁1");}}}).start();}
}

创建了两个线程,每个线程都试图按照不同的顺序获取两个锁lock1 和 lock2。这种锁的获取顺序不一致很容易导致死锁。

运行这段代码,果然卡住了。

运行 jstack pid 命令,可以看到死锁的线程信息。诚不欺我!

也可以使用一些可视化的性能监控工具,比如说 JConsole、VisualVM 等。

三分恶面渣逆袭:线程死锁检测

16. 聊聊线程同步和互斥?(补充)

互斥,就是不同线程通过竞争进入临界区(共享数据或者硬件资源),为了防止冲突,在有限的时间内只允许其中一个线程独占使用共享资源。如不允许同时写。

同步,就是多个线程彼此合作,通过一定的逻辑关系来共同完成一个任务。一般来说,同步关系中往往包含了互斥关系。同时,临界区的资源会按照某种逻辑顺序进行访问。如先生产后使用。

在 Java 中,当我们要保护一个资源时,通常会使用 synchronized 关键字或者 Lock 接口的实现类(如 ReentrantLock)来给资源加锁。

锁在操作系统层面的意思就是 Mutex(互斥),意思就是某个线程获取锁(进入临界区)后,其他线程不能再进入临界区,这样就达到了互斥的目的。

cxuan:使用临界区的互斥

锁要处理的问题大概有四种:

  • 谁拿到了锁,可以是当前 class,可以是某个 lock 对象,或者实例的 markword;
  • 抢占锁的规则,只能一个人抢 Mutex;能抢有限多次(Semaphore);自己可以反复抢(可重入锁 ReentrantLock);读可以反复抢,写只能一个人抢(读写锁ReadWriteLock);
  • 抢不到怎么办,等待,等待的时候怎么等,自旋,阻塞,或者超时;
  • 锁被释放了还有其他等待锁的怎么办?通知所有人一起抢或者只告诉一个人抢(Condition 的 signalAll 或者 signal)

恰当地使用锁,就能解决同步或者互斥的问题。

再补充一些。所谓同步,即协同步调,按预定的先后次序访问共享资源,以免造成混乱。

线程同步是多线程编程中的一个核心概念,它涉及到在多线程环境下如何安全地访问和修改共享资源的问题。

当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作,其他线程才能对该内存地址进行操作。

如果多个线程同时读写某个共享资源(如变量、文件等),而没有适当的同步机制,就可能导致数据不一致、数据损坏等问题的出现。

线程同步的实现方式有 6 种:互斥量、读写锁、条件变量、自旋锁、屏障、信号量。

  • 互斥量:互斥量(mutex)是一种最基本的同步手段,本质上是一把锁,在访问共享资源前先对互斥量进行加锁,访问完后再解锁。对互斥量加锁后,任何其他试图再次对互斥量加锁的线程都会被阻塞,直到当前线程解锁。
  • 读写锁读写锁有三种状态,读模式加锁、写模式加锁和不加锁;一次只有一个线程可以占有写模式的读写锁,但是可以有多个线程同时占有读模式的读写锁。非常适合读多写少的场景。
  • 条件变量条件变量是一种同步手段,它允许线程在满足特定条件时才继续执行,否则进入等待状态。条件变量通常与互斥量一起使用,以防止竞争条件的发生。
  • 自旋锁:自旋锁是一种锁的实现方式,它不会让线程进入睡眠状态,而是一直循环检测锁是否被释放。自旋锁适用于锁的持有时间非常短的情况。
  • 信号量:信号量(Semaphore)本质上是一个计数器,用于为多个进程提供共享数据对象的访问。
互斥和同步在时间上有要求吗?

互斥和同步在时间上是有一定要求的,因为它们都涉及到对资源的访问顺序和时机控制。

互斥的核心是保证同一时刻只有一个线程能访问共享资源或临界区。虽然互斥的重点不是线程执行的顺序,但它对访问的时间点有严格要求,以确保没有多个线程在同一时刻访问相同的资源。

同步强调的是线程之间的执行顺序和时间点的配合,特别是在多个线程需要依赖于彼此的执行结果时。例如,在 CountDownLatch 中,主线程会等待多个子线程的任务完成,子线程完成后才会减少计数,主线程会在计数器归零时继续执行。

class SyncExample {public static void main(String[] args) throws InterruptedException {CountDownLatch latch = new CountDownLatch(3);// 创建3个子线程for (int i = 0; i < 3; i++) {new Thread(() -> {try {Thread.sleep(1000); // 模拟任务System.out.println("打完王者了.");} catch (InterruptedException e) {e.printStackTrace();} finally {latch.countDown(); // 每个线程任务完成后计数器减1}}).start();}System.out.println("等打完三把王者就去睡觉...");latch.await(); // 主线程等待子线程完成System.out.println("好,王者玩完了,可以睡了");}
}

CountDownLatch

17. 聊聊悲观锁和乐观锁?(补充)

2024 年 05 月 01 日增补

对于悲观锁来说,它总是认为每次访问共享资源时会发生冲突,所以必须对每次数据操作加上锁,以保证临界区的程序同一时间只能有一个线程在执行。

悲观锁的代表有 synchronized 关键字Lock 接口

乐观锁,顾名思义,它是乐观派。乐观锁总是假设对共享资源的访问没有冲突,线程可以不停地执行,无需加锁也无需等待。一旦多个线程发生冲突,乐观锁通常使用一种称为 CAS 的技术来保证线程执行的安全性。

由于乐观锁假想操作中没有锁的存在,因此不太可能出现死锁的情况,换句话说,乐观锁天生免疫死锁。

  • 乐观锁多用于“读多写少“的环境,避免频繁加锁影响性能;
  • 悲观锁多用于”写多读少“的环境,避免频繁失败和重试影响性能。

相关文章:

Java并发编程面试题:锁(17题)

&#x1f9d1; 博主简介&#xff1a;CSDN博客专家&#xff0c;历代文学网&#xff08;PC端可以访问&#xff1a;https://literature.sinhy.com/#/?__c1000&#xff0c;移动端可微信小程序搜索“历代文学”&#xff09;总架构师&#xff0c;15年工作经验&#xff0c;精通Java编…...

各类神经网络学习:(四)RNN 循环神经网络(下集),pytorch 版的 RNN 代码编写

上一篇下一篇RNN&#xff08;中集&#xff09;待编写 代码详解 pytorch 官网主要有两个可调用的模块&#xff0c;分别是 nn.RNNCell 和 nn.RNN &#xff0c;下面会进行详细讲解。 RNN 的同步多对多、多对一、一对多等等结构都是由这两个模块实现的&#xff0c;只需要将对输入…...

【python】OpenCV—Hand Landmarks Detection

文章目录 1、功能描述2、代码实现3、效果展示4、完整代码5、涉及到的库函数6、参考 更多有趣的代码示例&#xff0c;可参考【Programming】 1、功能描述 基于 opencv-python 和 mediapipe 实现手部关键点的检测&#xff08;无法检测出手&#xff0c;不过可以根据关键点的信息外…...

C++和标准库速成(十)——类型别名、类型定义、类型推断和标准库简介

目录 1. 类型别名2. 类型定义(不建议)3. 类型推断3.1 auto3.1.1 auto&3.1.2 auto*3.1.3 拷贝列表初始化和直接列表初始化 3.2 decltype 4. 标准库简介参考 1. 类型别名 类型别名为现有的类型声明提供新名称。可以将类型别名视为用于为现有类型声明引入同义词而无须创建新类…...

Java JMX 未授权访问漏洞分析与修复指南

#作者&#xff1a;张桐瑞 文章目录 一、漏洞背景二、漏洞描述三、漏洞影响四、修复方案1. 禁用远程JMX访问&#xff1a;2. 配置JMX访问权限&#xff1a; 一、漏洞背景 Java管理扩展&#xff08;Java Management Extensions&#xff0c;简称JMX&#xff09;是Java平台的管理和…...

挂谷问题与挂谷猜想:从平面转针到高维拓扑

挂谷问题与挂谷猜想&#xff1a;从平面转针到高维拓扑 目录 挂谷问题的起源数学定义与基本性质研究进展挂谷集合与挂谷猜想王虹与Joshua Zahl的突破意义与影响 挂谷问题的起源 1917年&#xff0c;日本数学家挂谷宗一(かけや そういち Soichi Kakeya&#xff0c;1886-1947)提…...

区块链 智能合约安全 | 整型溢出漏洞

目录&#xff1a; 核心概念 溢出类型 上溢 原理 案例 下溢 原理 案例 练习 漏洞修复 使用 SafeMath 库&#xff08;旧版本&#xff09; 升级 Solidity 版本&#xff08;≥0.8.0&#xff09; 地址&#xff1a;zkanzz 整型溢出漏洞&#xff08;Integer Overflow/Underflow Vulne…...

C# HTTP 文件上传、下载服务器

程序需要管理员权限&#xff0c;vs需要管理员打开 首次运行需要执行以下命令注册URL&#xff08;管理员命令行&#xff09; netsh advfirewall firewall add rule name"FileShare" dirin actionallow protocolTCP localport8000 ipconfig | findstr "IPv4&quo…...

IDEA导入jar包后提示无法解析jar包中的类,比如无法解析符号 ‘log4j‘

IDEA导入jar包后提示无法解析jar包中的类 问题描述解决方法 问题描述 IDEA导入jar包的Maven坐标后&#xff0c;使用jar中的类比如log4j&#xff0c;仍然提示比如无法解析符号 log4j。 解决方法 在添加了依赖和配置文件后&#xff0c;确保刷新你的IDE项目和任何缓存&#xff…...

C++前缀和

个人主页&#xff1a;[PingdiGuo_guo] 收录专栏&#xff1a;[C干货专栏] 大家好&#xff0c;今天我们来了解一下C的一个重要概念&#xff1a;前缀和 目录 1.什么是前缀和 2.前缀和的用法 1.前缀和的定义 2.预处理前缀和数组 3.查询区间和 4.数组中某个区间的和是否为特定…...

kafka压缩

最近有幸公司参与kafka消息压缩&#xff0c;背景是日志消息量比较大。kafka版本2.4.1 一、确认压缩算法 根据场景不同选择不同。如果是带宽敏感患者推荐高压缩比的zstd&#xff0c;如果是cpu敏感患者推荐lz4 lz4和zstd底层都使用的是lz77算法&#xff0c;具体实现逻辑不同&am…...

C 语 言 --- 扫 雷 游 戏(初 阶 版)

C 语 言 --- 扫 雷 游 戏 初 阶 版 代 码 全 貌 与 功 能 介 绍扫雷游戏的功能说明游 戏 效 果 展 示游 戏 代 码 详 解game.htest.cgame.c 总结 &#x1f4bb;作 者 简 介&#xff1a;曾 与 你 一 样 迷 茫&#xff0c;现 以 经 验 助 你 入 门 C 语 言 &#x1f4a1;个 人 主…...

黑鲨外设2025春季新品发布会:全球首款“冷暖双控”鼠标亮相!

据可靠消息称&#xff0c;电竞外设领域的创新引领者——黑鲨外设&#xff0c;正式官宣将于2025年3月28日17:00召开主题为“究极体验&#xff0c;竞在其中”春季新品发布会。据悉&#xff0c;此次新品发布会将于黑鲨游戏外设和黑鲨游戏手机官方平台同步直播&#xff0c;&#xf…...

SpringBoot-MVC配置类与 Controller 的扫描

文章目录 前言一、自动配置类位置二、自动配置类解析2.1 WebMvcAutoConfiguration2.1.1 EnableWebMvcConfiguration 2.2 DispatcherServletAutoConfiguration 三、RequestMapping 的扫描过程3.1 RequestMappingHandlerMapping#afterPropertiesSet3.2 RequestMappingHandlerMapp…...

Nexus L2 L3基本配置

接口基本配置 N7K上所有端口默认处于shutdown状态; N5K上所有端口默认处于no shutdown状态(所有端口都是switchport) 默认所有接口都是三层route模式, 只有当线卡不支持三层的时候, 接口才会处于二层switchport模式 show run all | in “system default” 创建SVI口需要提前打…...

asp.net 4.5在医院自助系统中使用DeepSeek帮助医生分析患者报告

环境&#xff1a; asp.net 4.5Visual Studio 2015本地已经部署deepseek-r1:1.5b 涉及技术 ASP.NET MVC框架用于构建Web应用程序。使用HttpWebRequest和HttpWebResponse进行HTTP请求和响应处理。JSON序列化和反序列化用于构造和解析数据。SSE&#xff08;服务器发送事件&#xf…...

LCCI ESG 中英联合认证国际分析师适合的岗位

LCCI ESG中英联合认证国际分析师领域热门岗位大揭秘&#xff01;&#x1f30d; 大家好&#xff01;今天我们来探讨LCCI ESG中英联合认证国际分析师领域的热门岗位&#xff0c;看看是否有适合你的选择。 1️⃣ LCCI ESG中英联合认证国际分析师报告专员&#xff1a;主要负责编制…...

AGI成立的条件

AGI&#xff08;通用人工智能&#xff09;的成立需满足多项核心条件&#xff0c;这些条件既涵盖技术能力层面的突破&#xff0c;也涉及伦理与认知维度的考量。 一、通用性与多任务处理能力 跨领域泛化能力 AGI需具备类似人类的通用性&#xff0c;能够灵活切换不同领域&#xf…...

论文阅读:2023 EMNLP SeqXGPT: Sentence-level AI-generated text detection

总目录 大模型安全相关研究&#xff1a;https://blog.csdn.net/WhiffeYF/article/details/142132328 SeqXGPT: Sentence-level AI-generated text detection https://aclanthology.org/2023.emnlp-main.73/ https://github.com/Jihuai-wpy/SeqXGPT https://www.doubao.com/…...

解决python配置文件类configparser.ConfigParser,插入、读取数据,自动转为小写的问题

配置类 [Section1] Key_AAA Value[Section2] AnotherKey Value默认情况下&#xff0c;ConfigParser会将ini配置文件中的KEY&#xff0c;转为小写。 重载后配置类&#xff1a; 继承类从configparser.ConfigParser改为configparser.RawConfigParser重载方法optionxform&#…...

超图神经网络的详细解析与python示例

扩展传统集合关系至超边结构&#xff0c;处理高阶交互问题。 有关人工智能的数学基础之逻辑、集合论和模糊理论&#xff1a;看我文章人工智能的数学基础之逻辑、集合论和模糊理论-CSDN博客 一、超图神经网络概述 超图神经网络&#xff08;Hypergraph Neural Network&#xff0…...

机器视觉中图像的腐蚀和膨胀是什么意思?它能用来做什么?

​腐蚀&#xff08;Erosion&#xff09;​和膨胀&#xff08;Dilation&#xff09;​是两种基本的形态学操作&#xff0c;通常用于二值图像&#xff08;黑白图像&#xff09;的处理。它们是形态学图像处理的基础&#xff0c;广泛应用于图像分割、边缘检测、噪声去除等任务。 1…...

破局 MySQL 死锁:深入理解锁机制与高效解决方案

死锁的原理 1. 什么是死锁&#xff1f; 当 多个事务 在并发执行时&#xff0c;每个事务都 持有其他事务需要的锁&#xff0c;同时又在 等待对方释放锁&#xff0c;导致所有事务都无法继续执行的状态&#xff0c;称为 死锁&#xff08;Deadlock&#xff09;。 2. 死锁的四个必要…...

机器学习——分类、回归、聚类、LASSO回归、Ridge回归(自用)

纠正自己的误区&#xff1a;机器学习是一个大范围&#xff0c;并不是一个小的方向&#xff0c;比如&#xff1a;线性回归预测、卷积神经网络和强化学都是机器学习算法在不同场景的应用。 机器学习最为关键的是要有数据&#xff0c;也就是数据集 名词解释&#xff1a;数据集中的…...

脚本语言 Lua

概念 Lua由标准C编写而成&#xff0c;几乎在所有操作系统和平台上都可以编译、运行。Lua脚本可以很容易地被C/C 代码调用&#xff0c;也可以反过来调用C/C的函数&#xff0c;这使得Lua在应用程序中可以被广泛应用。Lua并没有提供强大的库&#xff0c;它是不适合作为开发独立应…...

Spring相关面试题

目录 Spring中常用的注解有哪些 Spring Boot中RestController和Controller注解有什么区别&#xff1f; Spring的注解requestBody和responseBody的区别 说说Bean和componentscan的区别 简单介绍一下springboot Spring Boot有哪些常用的Starter依赖&#xff1f; 说说sprin…...

Python学习- 数据结构类型

一. list list_data [10, 20, 30]列表&#xff1a;是一个不限制类型&#xff0c;可增加&#xff0c;修改&#xff0c;删除的数据类型 可操作的方法&#xff1a;append&#xff0c;extend, pop&#xff0c;del &#xff0c;insert append: 可向list最后一个位置添加一个元…...

Azure Delta Lake、Databricks和Event Hubs实现实时欺诈检测

设计Azure云架构方案实现Azure Delta Lake和Azure Databricks&#xff0c;结合 Azure Event Hubs/Kafka 摄入实时数据&#xff0c;通过 Delta Lake 实现 Exactly-Once 语义&#xff0c;实时欺诈检测&#xff08;流数据写入 Delta Lake&#xff0c;批处理模型实时更新&#xff0…...

【从零开始学习计算机科学】软件测试(十)嵌入式系统测试、游戏开发与测试过程、移动应用软件测试 与 云应用软件测试

【从零开始学习计算机科学】软件测试(十)嵌入式系统测试、游戏开发与测试过程、移动应用软件测试 与 云应用软件测试 嵌入式系统测试测试策略及测试流程嵌入式软件测试问题及测试方法嵌入式软件的测试流程游戏开发与测试过程游戏开发与通用软件的开发过程区别游戏测试主要内容…...

C#零基础入门篇(18. 文件操作指南)

## 一、文件操作基础 在C#中&#xff0c;文件操作主要通过System.IO命名空间中的类来实现&#xff0c;例如File、FileStream、FileInfo等。 ## 二、常用文件操作方法 ### &#xff08;一&#xff09;文件读取 1. **使用File.ReadAllText方法读取文件内容为字符串** …...

深入探究 JVM 堆的垃圾回收机制(一)— 判活

垃圾回收分为两步&#xff1a;1&#xff09;判定对象是否存活。2&#xff09;将“消亡”的对象进行内存回收。 1 判定对象存活 可达性分析算法&#xff1a;通过一系列“GC Roots”对象作为起始节点集&#xff0c;从这些节点开始&#xff0c;根据引用关系向下搜索&#xff0c;…...

SQL优化主要有哪些方式

对经常查询的区分度高的条件字段建立索引&#xff0c;也就是用在where条件里的字段。使用没有建立索引的非主键字段作为条件查询时&#xff0c;会进行全表扫描&#xff0c;因为这个字段的数据分步是不规律的&#xff0c;但是需要避免在频繁更新的字段上建立索引&#xff0c;因为…...

基于Spring Boot的公司资产网站的设计与实现(LW+源码+讲解)

专注于大学生项目实战开发,讲解,毕业答疑辅导&#xff0c;欢迎高校老师/同行前辈交流合作✌。 技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、小程序、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容&#xff1a;…...

笔记本电脑关不了机是怎么回事 这有解决方法

在快节奏的现代生活中&#xff0c;笔记本电脑已成为我们工作、学习和娱乐的得力助手。在使用电脑的过程中&#xff0c;笔记本电脑突然关不了机了&#xff0c;怎么回事&#xff1f;下面驱动人生就来讲一讲笔记本电脑不能正常关机的解决方法&#xff0c;有需要的可以来看看。 一、…...

OSPF 协议详解:从概念原理到配置实践的全网互通实现

什么是OSPF OSPF&#xff08;开放最短路径优先&#xff09;是由IETF开发的基于链路状态的自治系统内部路由协议&#xff0c;用来代替存在一些问题的RIP协议。与距离矢量协议不同&#xff0c;链路状态路由协议关心网络中链路活接口的状态&#xff08;包括UP、DOWN、IP地址、掩码…...

【C++】多态

目录 文章目录 前言 一、多态的概念 二、多态的定义及实现 三、重载/重写/隐藏的对比 四、纯虚函数和抽象类 五、多态的原理 总结 前言 本文主要讲述C中的多态&#xff0c;涉及的概念有虚函数、协变、纯虚函数、抽象类、虚表指针和虚函数表等。 一、多态的概念 多态分…...

CentOS 8 停止维护后通过 rpm 包手动安装 docker

根据 Docker官方文档 的指引&#xff0c;进入 Docker rpm 包下载的地址&#xff0c;根据自己系统的架构和具体版本选择对应的路径 这里使用 Index of linux/centos/7/x86_64/stable/ 版本&#xff0c;根据 docker 官方的给出的安装命令选择性的下载对应的 rpm 包 最终使用 yum …...

STT-MRAM CIM 赋能边缘 AI:高性能噪声鲁棒贝叶斯神经网络宏架构详解

引言 近年来&#xff0c;基于卷积神经网络&#xff08;CNN&#xff09;和视觉转换器&#xff08;ViT&#xff09;的存算一体&#xff08;CIM&#xff09;边缘AI设备因其低延迟、高能效、低成本等性能受到越来越广泛的关注。然而&#xff0c;当环境中存在噪声时&#xff08;例如…...

Performance Hub Active Report

Performance Hub 是 Oracle Enterprise Manager Database Express &#xff08;EM Express&#xff09; 中的一项功能&#xff0c;可提供给定时间范围内所有性能数据的新整合视图。用户可以使用 Database Express 页面顶部的时间选择器选择时间范围&#xff0c;详细信息选项卡将…...

小白闯AI:Llama模型Lora中文微调实战

文章目录 0、缘起一、如何对大模型进行微调二、模型微调实战0、准备环境1、准备数据2、模型微调第一步、获取基础的预训练模型第二步:预处理数据集第三步:进行模型微调第四步:将微调后的模型保存到本地4、模型验证5、Ollama集成部署6、结果测试三、使用总结AI是什么?他应该…...

【数学建模】TOPSIS法简介及应用

文章目录 TOPSIS法的基本原理TOPSIS法的基本步骤TOPSIS法的应用总结 在 多目标决策分析中&#xff0c;我们常常需要在多个选择中找到一个最优解。 TOPSIS&#xff08;Technique for Order Preference by Similarity to Ideal Solution&#xff09;法是一个广泛应用的决策方法…...

优选算法训练篇08--力扣15.三数之和(难度中等)

目录 1.题目链接&#xff1a;15.三数之和 2.题目描述&#xff1a; 3.解法(排序双指针) 1.题目链接&#xff1a;15.三数之和 2.题目描述&#xff1a; 给你一个整数数组 nums &#xff0c;判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i ! j、i ! k 且 j ! k &…...

【docker】--- 详解 WSL2 中的 Ubuntu 和 Docker Desktop 的区别和关系!

在编程的艺术世界里,代码和灵感需要寻找到最佳的交融点,才能打造出令人为之惊叹的作品。而在这座秋知叶i博客的殿堂里,我们将共同追寻这种完美结合,为未来的世界留下属于我们的独特印记。【WSL 】--- Windows11 迁移 WSL 超详细指南 —— 给室友换一个宿舍! 开发环境一、引…...

RAG 架构地基工程-Retrieval 模块的系统设计分享

目录 一、知识注入的关键前奏——RAG 系统中的检索综述 &#xff08;一&#xff09;模块定位&#xff1a;连接语言模型与知识世界的桥梁 &#xff08;二&#xff09;核心任务&#xff1a;四大关键问题的协调解法 &#xff08;三&#xff09;系统特征&#xff1a;性能、精度…...

解决stm32引脚如果选择输入模式

1. 输入模式分类 STM32的GPIO输入模式主要分为以下四种&#xff1a; 浮空输入&#xff08;Floating Input / Input Floating&#xff09; 上拉输入&#xff08;Input Pull-Up&#xff09; 下拉输入&#xff08;Input Pull-Down&#xff09; 模拟输入&#xff08;Analog Inp…...

Java 填充 PDF 模版

制作 PDF 模版 安装 OnlyOffice 从 OnlyOffice 官网下载 OnlyOffice Desktop&#xff0c;安装过程很简单&#xff0c;一路下一步即可。用 OnlyOffice 制作 PDF 模版&#xff08;表单&#xff09; 使用 OnlyOffice 表单设计器&#xff0c;制作表单&#xff0c;如下图 注意命名…...

Maven安装与环境配置

首先我们先介绍一些关于Maven的知识&#xff0c;如果着急直接看下面的安装教程。 目录 Maven介绍 Maven模型 Maven仓库 Maven安装 下载 安装步骤 Maven介绍 Apache Maven是一个项目管理和构建工具&#xff0c;它基于项目对象模型(Project Object Model , 简称: POM)的概念…...

鸿蒙HarmonyOS NEXT应用崩溃分析及修复

鸿蒙HarmonyOS NEXT应用崩溃分析及修复 如何保证应用的健壮性&#xff0c;其中一个指标就是看崩溃率&#xff0c;如何降低崩溃率&#xff0c;就需要知道存在哪些崩溃&#xff0c;然后对症下药&#xff0c;解决崩溃。那么鸿蒙应用中存在哪些崩溃类型呢&#xff1f;又改如何解决…...

基于PySide6的CATIA自动化工具开发实战——空几何体批量清理系统

一、功能概述 本工具通过PySide6构建用户界面&#xff0c;结合PyCATIA库实现CATIA V5的自动化操作&#xff0c;提供两大核心功能&#xff1a; ​空几何体清理&#xff1a;智能识别并删除零件文档中的无内容几何体&#xff08;Bodies&#xff09;​空几何图形集清理&#xff1…...

【CSS文字渐变动画】

CSS文字渐变动画 HTML代码CSS代码效果图 HTML代码 <div class"title"><h1>今天是春分</h1><p>正是春天到来的日子&#xff0c;花都开了&#xff0c;小鸟也飞回来了&#xff0c;大山也绿了起来&#xff0c;空气也有点嫩嫩的气息了</p>…...