Java 同步机制方法及其部分源码解读

1. synchronized 关键字 互斥锁

使用关键字synchronized修饰的方法或代码块,可以保证其修饰内容在任何时刻,最多只有一个线程可以访问,可用于控制多个线程在访问共享资源时的并发问题。

每个对象都有一把锁,每个synchronized修饰的方法都必须获得该对象的锁才能够执行,否则线程阻塞;一旦获得锁之后,就会独占该锁,直到该方法返回才能释放锁;

同步方法对应的锁是this或其它对象;同步代码块对应的锁可以是this也可以是其它对象或变量;而静态的同步方法的锁为当前类本身,因为静态方法是属于类的而不属于对象

使用synchronized关键字的多种场景:

public class TestSynchronized {private Test test;public TestSynchronized (Test test) {this.test = test;}// 1. 同步代码块 锁对象本质就是需要增删改的对象public void TestBlock {// 锁对象为 thissynchronized (this) {// 同步代码块}// 锁对象为 Object lockObject lock = new Object();synchronized (lock) {// 同步代码块}// 锁对象为 其它类synchronized (test) {// 同步代码块}}// 2. 同步方法public synchronized void TestMethods() {// 默认锁对象为:this// 同步方法体}// 3. 同步静态方法public synchronized static void TestStaticMethods() {// 默认锁对象为:当前类.class// 同步方法体}	// 4. 静态方法内的同步代码块public static void TestStaticBlock {// 在静态方法内,同步代码块时就无法用某个对象来上锁了,只用能整个类上锁synchronized (TestSynchronized.class) {// 同步代码块} }
}public void Test {private int temp;
}

特性:

  1. 可重入锁:

synchronized 关键字实现的是可重入锁。这意味着,在同步方法或同步代码块内可以在不释放这个锁的情况下,再次进入同一个锁保护的区域。这通常用于处理递归方法调用的情况。

Object lock = new Object();
synchronized (lock) {System.out.println("First time acquiring it");synchronized (lock) {System.out.println("Entering again");synchronized (lock) {System.out.println("And again");}}
}
  1. 不公平性

synchronized 关键字默认情况下不保证公平性,即没有确定的顺序来分配锁。而ReentrantLock类提供了一个公平性参数,可以创建一个公平的锁,确保等待时间最长的线程最先获取锁。


2. ReentrantLock 互斥锁

相比synchronized这种隐式定义同步锁,JDK5.0提供了显示定义同步锁ReentrantLock,可以进行显式上锁、解锁,且使用更加灵活(显式:需要手动开启和关闭锁,需要记得及时关闭锁;隐式:出了作用域范围自动释放);

  • Lock只能对代码块上锁,而synchronized有代码块锁和方法锁;
  • 使用ReentrantLock可以精确控制锁的获取和释放,并且支持更多的高级特性,比如可中断锁、超时获取锁、公平性等。
  • ReentrantLocksynchronized都是可重入锁;
  • ReentrantLock类实现了 Lock接口以及Serializable接口,其中Lock接口源码如下,ReentrantLock对其中抽象方法进行了实现:
package java.util.concurrent.locks;
import java.util.concurrent.TimeUnit;public interface Lock {void lock();void lockInterruptibly() throws InterruptedException;boolean tryLock();boolean tryLock(long time, TimeUnit unit) throws InterruptedException;void unlock();Condition newCondition();
}

2.1. ReentrantLock构造器

  • 当设置true时,锁有利于授予访问最长等待的线程。否则,该锁不保证任何特定的访问顺序。

ReentrantLock构造器

public ReentrantLock()	// 创建一个ReentrantLock的实例,这相当于使用ReentrantLock(false) 
public ReentrantLock(boolean fair)	// 根据给定的公平政策创建一个 ReentrantLock的实例

ReentrantLock 有两个构造函数。第一个是无参构造函数,它创建一个非公平锁。第二个构造函数接受一个布尔值,如果为 true,则创建一个公平锁,否则创建一个非公平锁。公平锁会按照线程请求锁的顺序来获取锁。构造器源码如下:

/*** Creates an instance of {@code ReentrantLock}.* This is equivalent to using {@code ReentrantLock(false)}.*/
public ReentrantLock() {sync = new NonfairSync();
}/*** Creates an instance of {@code ReentrantLock} with the* given fairness policy.** @param fair {@code true} if this lock should use a fair ordering policy
*/
public ReentrantLock(boolean fair) {sync = fair ? new FairSync() : new NonfairSync();
}

其中SyncReentrantLock定义的抽象内部类,它继承自 AbstractQueuedSynchronizer(AQS)。AQS是一个用于构建锁和同步器的框架,它使用一个整数状态来表示同步状态,并提供了用于阻塞和唤醒线程的机制。AQS 维护了一个同步状态(通过getStatesetState方法访问),以及一个等待队列(用于阻塞和唤醒线程)。

SyncReentrantLock的核心组件,负责实现锁的获取和释放逻辑。Sync类的子类NonfairSyncFairSync通过实现tryAcquirelock方法来定义锁的具体行为。在ReentrantLock的情况下,Sync类还提供了locknonfairTryAcquire方法的实现,具体源码如下:

    abstract static class Sync extends AbstractQueuedSynchronizer {// 序列化ID,用于确保序列化和反序列化时的兼容性private static final long serialVersionUID = -5179523762034025860L;/*** Performs {@link Lock#lock}. The main reason for subclassing* is to allow fast path for nonfair version.*/// 在 ReentrantLock 中,有两个子类 FairSync(公平锁)和 NonfairSync(非公平锁),它们提供了 lock 方法的具体实现abstract void lock();/*** Performs non-fair tryLock.  tryAcquire is implemented in* subclasses, but both need nonfair try for trylock method.*/// 提供了非公平尝试获取锁的逻辑// 是 tryAcquire 方法的一个辅助方法,用于在非公平锁和公平锁的实现中尝试获取锁final boolean nonfairTryAcquire(int acquires) {// 获取当前线程final Thread current = Thread.currentThread();// 获取当前锁的状态int c = getState();// 如果状态为 0(即锁未被任何线程持有),则尝试使用 compareAndSetState 原子地将状态从 0 变更为 acquires(表示获取锁的线程数)。// 如果成功,设置当前线程为锁的独占拥有者,并返回 true。if (c == 0) {if (compareAndSetState(0, acquires)) {setExclusiveOwnerThread(current);return true;}}// 如果状态不为 0,并且当前线程已经是锁的独占拥有者,则增加锁的持有计数。else if (current == getExclusiveOwnerThread()) {int nextc = c + acquires;// 如果新的计数超过最大值,则抛出相应错误信息if (nextc < 0) // overflowthrow new Error("Maximum lock count exceeded");// 否则,更新状态并返回 true。setState(nextc);return true;}// 以上判断条件都不满足,返回 false 表示获取锁失败。return false;}// 尝试释放锁protected final boolean tryRelease(int releases) {// 计算新的锁状态:当前状态减去释放次数int c = getState() - releases;// 如果当前线程不是锁的拥有者,则抛出异常(只有锁的拥有者才能释放锁)if (Thread.currentThread() != getExclusiveOwnerThread())throw new IllegalMonitorStateException();// 表示锁是否已经完全释放boolean free = false;// 如果新的锁状态为0,表示锁完全释放,将 free 标志设置为 trueif (c == 0) {free = true;// 锁被完全释放,将锁的独占拥有者更新为 null setExclusiveOwnerThread(null);}// 更新锁状态setState(c);return free;}// 检查当前线程是否独占持有锁protected final boolean isHeldExclusively() {// While we must in general read state before owner,// we don't need to do so to check if current thread is ownerreturn getExclusiveOwnerThread() == Thread.currentThread();}// 用于创建一个新的 ConditionObject 实例,是 Condition 接口的实现,用于管理等待和通知机制,// 允许线程在条件不满足时挂起,并在条件变为真时被唤醒。final ConditionObject newCondition() {return new ConditionObject();}// Methods relayed from outer class// 以下方法从 ReentrantLock 外部类转发调用到 Sync 内部类// 返回锁的当前拥有者final Thread getOwner() {return getState() == 0 ? null : getExclusiveOwnerThread();}// 返回当前线程持有锁的次数final int getHoldCount() {return isHeldExclusively() ? getState() : 0;}// 检查锁是否被持有final boolean isLocked() {return getState() != 0;}/*** Reconstitutes the instance from a stream (that is, deserializes it).*/// Sync 类的序列化构造函数// 它在反序列化时被调用,以确保锁的状态被正确重置为未锁定状态private void readObject(java.io.ObjectInputStream s)throws java.io.IOException, ClassNotFoundException {// 恢复对象的字段状态s.defaultReadObject();// 将锁状态设置为0,表示锁未被持有setState(0); // reset to unlocked state}}

接着往下看Sync的实现类NonfairSyncFairSync的源码:

    /*** Sync object for non-fair locks*/static final class NonfairSync extends Sync {// Sync 类的序列化ID,用于确保序列化和反序列化时的兼容性。private static final long serialVersionUID = 7316153563782823691L;/*** Performs lock.  Try immediate barge, backing up to normal* acquire on failure.*/final void lock() {// 尝试通过原子操作 compareAndSetState,将锁的状态从 0 更改为 1(将未锁定的锁改为已锁定的锁)if (compareAndSetState(0, 1))// 将当前线程设置为锁的独占拥有者setExclusiveOwnerThread(Thread.currentThread());// 若这个原子操作失败(即锁已经被其他线程持有),则调用 acquire 方法来尝试正常获取锁elseacquire(1);}// 直接调用 Sync 类中定义的非公平尝试获取锁的 nonfairTryAcquire 方法protected final boolean tryAcquire(int acquires) {return nonfairTryAcquire(acquires);}}
    /*** Sync object for fair locks*/static final class FairSync extends Sync {// 序列化ID,用于确保序列化兼容性private static final long serialVersionUID = -3000897897090466540L;// 获取锁。这里的参数 1 表示获取一个锁许可// 如果锁没有被另一个线程占用并且立即返回,则将锁定计数设置为1。// 如果当前线程已经保持锁定,则保持计数增加1,该方法立即返回(可重入锁特性)。final void lock() {acquire(1);}/*** Fair version of tryAcquire.  Don't grant access unless* recursive call or no waiters or is first.*/protected final boolean tryAcquire(int acquires) {// 获取当前线程(current)final Thread current = Thread.currentThread();// 获取当前锁的状态int c = getState();// 若锁未被任何线程持有if (c == 0) {// 检查是否有线程在等待队列中// 若没有等待的线程,则尝试通过 compareAndSetState 原子地将状态从 0 变更为 acquiresif (!hasQueuedPredecessors() &&compareAndSetState(0, acquires)) {// 设置当前线程为锁的独占拥有者,并返回 truesetExclusiveOwnerThread(current);return true;}}// 如果状态不为 0,并且当前线程已经是锁的独占拥有者else if (current == getExclusiveOwnerThread()) {// 增加锁的持有计数int nextc = c + acquires;// 若新的计数超过最大值,则抛出错误if (nextc < 0)throw new Error("Maximum lock count exceeded");// 更新锁的新状态并返回setState(nextc);return true;}return false;}}

2.2. ReentrantLock常用方法

void lock()		// 获得锁
void unlock()	// 尝试释放此锁// 只有在调用时它不被另一个线程占用才能获取锁
boolean	tryLock()	
// 如果在给定的等待时间内没有被另一个线程占用,并且当前线程尚未被保留,则获取该锁。
boolean	tryLock(long timeout, TimeUnit unit)boolean	isFair()	// 如果此锁的公平设置为true,则返回 true 。
boolean	isLocked()	// 查询此锁是否由任何线程持有。

ReentrantLock常用方法简单实例

import java.util.concurrent.locks.ReentrantLock;// 1. 创建锁
ReentrantLock lock = new ReentrantLock();// 2. 上锁解锁
lock.lock();
// 临界区代码
lock.unlock();// 3. 尝试非阻塞地加锁:
boolean isLocked = lock.tryLock();
if (isLocked) {// 临界区代码lock.unlock();
}// 4. 带超时的尝试加锁:
long timeout = 1000; // 1秒
if (lock.tryLock(timeout, TimeUnit.MILLISECONDS)) {try {// 临界区代码} finally {lock.unlock();}
}// 5. 可重入加锁:
lock.lock();
try {// 可以再次获取同一把锁,而不会被阻塞lock.lock();// 临界区代码
} finally {lock.unlock();
}

2.3. lock()方法

具体上锁逻辑,需要看最开始选择了哪一个构造器来实例化ReentrantLock,是公平锁还是非公平锁;

void lock()		
// 获得锁
// 如果锁没有被另一个线程占用并且立即返回,则将锁定计数设置为1。
// 如果当前线程已经保持锁定,则保持计数增加1,该方法立即返回(可重入锁特性)。
// 如果锁被另一个线程保持,则当前线程将被禁用以进行线程调度,并且在锁已被获取之前处于休眠状态,此时锁定保持计数被设置为1。

2.4. unlock()方法

void unlock()	
// 尝试释放此锁
// 如果当前线程是该锁的持有者,则保持计数递减(可重入机制)
// 如果保持计数现在为零,则锁被释放。 
// 如果当前线程不是该锁的持有者,则抛出IllegalMonitorStateException(表示当前线程不持有此锁)。

2.5. trylock()方法

boolean	tryLock()	
// 只有在调用时它不被另一个线程占用才能获取锁
// 如果没有被另一个线程保持,则获取锁定,并立即返回值为true ,将锁定保持计数设置为1。
// 如果当前线程已经保存该锁,则保持计数增加1,该方法返回true 。
// 如果锁由另一个线程持有,则该方法将立即返回值为false 。

3. Semaphore 记录型信号量

semaphore是一种计数器,用来管理一定数量的许可(permit)。它允许多个线程同时访问某些资源,如连接池、对象池等;

相比synchronizedLock主要解决多个线程访问同一资源造成的数据不一致,semaphore实现了资源的多副本的并发访问控制,可以限制同时访问特定资源的线程数量。此外semaphore 允许任何线程释放许可,并不是只有拥有线程才能释放资源;

  • Semaphore 适用于需要管理资源访问数量的场景,特别是在限制同时访问资源的线程数量时非常有用。
  • Lock 提供了更多的灵活性和控制能力,适用于需要更复杂同步逻辑的场景。
  • synchronized 则因其简单性,适用于基本的同步需求。

3.1. semaphore构造器

Semaphore 可以是公平的或非公平的。公平的 Semaphore 会考虑线程的等待时间来分配许可,而非公平的 Semaphore 不考虑这一点。

// 创建一个 Semaphore 与给定数量的许可证和非公平公平设置。
Semaphore(int permits)	
// 创建一个 Semaphore 与给定数量的许可证和给定的公平设置。
Semaphore(int permits, boolean fair) // 实例
Semaphore semaphore = new Semaphore(10, true);// 信号量构造器源码
// 非公平信号量
public Semaphore(int permits) {sync = new NonfairSync(permits);
}// 公平信号量
public Semaphore(int permits, boolean fair) {sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}

公平信号量FairSync和非公平信号量NofairSync都是对Semaphore内部抽象类Sync的实现(与ReentrantLock源码类似)

    abstract static class Sync extends AbstractQueuedSynchronizer {// Sync 类的序列化ID,用于确保序列化和反序列化时的兼容性。private static final long serialVersionUID = 1192457210091910933L;// 构造函数// setState 继承自AbstractQueuedSynchronizer(AQS),其中的 State 是 AQS的// private volatile int state; 用于表示同步状态,在不同的同步机制中对应的意义不用// 在 Semaphore 中 State 表示可用的许可Sync(int permits) {setState(permits);}// 返回当前信号量可用的许可数量final int getPermits() {return getState();}// 非公平信号量尝试获取 acquires 个许可final int nonfairTryAcquireShared(int acquires) {// 表示无限循环,这个方法会一直尝试直到成功修改状态或者确定无法获取更多的许可。for (;;) {// 获取当前可用的许可数量int available = getState();// 当前可用许可减去申请许可后的剩余许可数量int remaining = available - acquires;// 若当前许可数量不足以满足请求,就直接返回 remaining,不允许这次请求// 若当前许可数量满意请求(remaining >= 0),就进行状态修改,允许该请求,然后再返回 remaining// 修改状态使用的是原子操作 compareAndSetState 将当前状态 available 修改为 remainingif (remaining < 0 ||compareAndSetState(available, remaining))return remaining;}}// 用于尝试释放 releases 个许可protected final boolean tryReleaseShared(int releases) {// 不断循环,直到操作成功或者出现异常才停止for (;;) {// 当前状态int current = getState();// 释放后的新状态值int next = current + releases;// 若新状态值导致许可数量上溢出,抛出对应错误if (next < current) // overflowthrow new Error("Maximum permit count exceeded");// 同样通过原子操作将当前状态 current 修改为 nextif (compareAndSetState(current, next))return true;}}// 用于减少 reductions 个许可final void reducePermits(int reductions) {for (;;) {// 当前可用许可int current = getState();// 减少后的许可数量int next = current - reductions;// 若减少后的新状态发生了下溢出,抛出对应错误if (next > current) // underflowthrow new Error("Permit count underflow");// 将当前状态 current 修改为 nextif (compareAndSetState(current, next))return;}}// 用于释放所有许可// 通常用于在信号量被销毁或者不再需要时,确保所有的许可都被释放,避免资源泄露。final int drainPermits() {for (;;) {int current = getState();// 如果当前可用许可已经为0,说明没有更多的许可需要释放,就直接返回当前状态// 否则就通过原子操作将当前状态更改为0,然后再返回当前的状态值if (current == 0 || compareAndSetState(current, 0))return current;}}}

接下来看看Sync抽象类的具体实现,非公平信号量NofairSync和公平信号量FairSync的源码,类内部没有声明的方法都是直接继承Sync中声明;

    /*** NonFair version*/static final class NonfairSync extends Sync {private static final long serialVersionUID = -2694183684443567898L;// 调用父类的构造函数NonfairSync(int permits) {super(permits);}// 尝试非公平地获取 acquires 个许可,直接使用父类的方法// 在获取许可时不考虑线程等待队列,这可能会允许后来的线程先获取许可。protected int tryAcquireShared(int acquires) {return nonfairTryAcquireShared(acquires);}}/*** Fair version*/static final class FairSync extends Sync {private static final long serialVersionUID = 2014338818796000944L;// 调用父类的构造函数FairSync(int permits) {super(permits);}// 尝试公平地获取 acquires 个许可// 只有当该线程在资源等待队列中没有前驱线程,才能够进行许可授予protected int tryAcquireShared(int acquires) {for (;;) {// 检查当前线程是否有排队的前驱线程。如果有,返回 -1 表示不授予许可。if (hasQueuedPredecessors())return -1;// 当前可用的许可int available = getState();// 计算尝试获取许可后剩余的许可数量int remaining = available - acquires;// 若当前许可数量不足以满足请求,就直接返回 remaining,不允许这次请求// 若当前许可数量满意请求(remaining >= 0),就进行状态修改,允许该请求,然后再返回 remainingif (remaining < 0 ||compareAndSetState(available, remaining))return remaining;}}}

3.2. semaphore常用方法

// 从该信号量获取许可证,阻塞直到许可可用,或线程为 interrupted 。
void acquire()
// 从该信号量获取给定数量的许可证,阻塞直到所有许可可用,否则线程为 interrupted 。
void acquire(int permits)// 返回此信号量中当前可用的许可数。
int	availablePermits()
// 缩小可用许可证的数量。
protected void	reducePermits(int reduction)// 释放许可证,将其返回到信号量。
void release()
// 释放给定数量的许可证,将其返回到信号量。
void release(int permits)// 获取并返回所有可立即获得的许可。
int	drainPermits()

4. volatile 关键字

volatile 是 Java 中的一个关键字,用于声明一个变量在多线程环境中的可见性。当一个变量被声明为 volatile时,它保证每次访问变量时都会从主内存中读取,而不是从线程的工作内存中读取;同样,当变量被修改时,新值也会立即写入到主内存中,以确保其他线程能够看到最新的值。

4.1. volatile 特征

  1. 内存可见性:

保证此变量对所有的线程的可见性:当一个线程修改了某一个volatile变量的值,其他线程能够立即看到该变化;

  1. 有序性:

Java 内存模型(JMM)会禁止volatile变量前后的读写操作进行指令重排序。这确保了volatile变量的写操作在读操作之前完成,从而保证了程序的执行顺序。具体实现是通过对volatile变量的读写操作前后加上各种特定的内存屏障来禁止指令重排序来保障有序性;

  1. 重排序是指编译器和处理器为了优化程序性能面对指令序列进行重新排序的一种手段,有时候会改变程序予以的先后顺序。
  2. 内存屏障就是:在内存屏障之前的指令全部执行完成之后,才允许执行内存屏障之后的指令,从而保证代码的顺序性。具体原理如下:
    内存屏障可以分为两种:Load Barrier 读屏障和 Store Barrier 写屏障。
  • 在读指令之前可以插入读屏障,让工作内存或CPU高速缓存当中的缓存数据失效,重新回到主内存中获取最新数据。
  • 在写指令之后可以插入写屏障,强制把写缓冲区的数据刷回到主内存中,让最新数据写入主存,使得其他线程可见。
  1. 非原子性

volatile变量的复合操作(如volatile++)是不具有原子性的


Reference

  1. Java 并发基础 volatile 关键字
  2. 必须了解的内存屏障
  3. Java中的volatile

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

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

相关文章

优秀企业都在用的企微知识库,再不搭建就晚了!

每个团队都在寻找让工作效率提升的方法。如果你想知道哪些团队能够高效地完成任务&#xff0c;而另一些却步履维艰&#xff0c;那么答案可能就是“企业微信知识库”。见过很多团队都在使用它&#xff0c;而且效果非常显著。如果你还没有搭建属于自己的企微知识库&#xff0c;可…

你真的会写接口自动化测试断言吗?

你真的会写自动化测试断言吗&#xff1f; 在接口测试中&#xff0c;断言是一项非常重要的操作&#xff0c;它是用来校验接口返回结果是否符合预期的一种手段。一般来说&#xff0c;接口测试断言大致可以分为以下几类&#xff1a; 状态码断言&#xff1a;这是最基本也是最常用的…

【C语言】汉诺塔问题

目录 一、何为汉诺塔问题&#xff1f; 二、汉诺塔计算规律 三、打印汉诺塔的移动路径 总结 一、何为汉诺塔问题&#xff1f; 汉诺塔问题是一个经典的问题。汉诺塔&#xff08;Hanoi Tower&#xff09;&#xff0c;又称河内塔&#xff0c;源于印度一个古老传说。大梵天创造世…

16_I2C库函数

I2C库函数 1.void I2C_DeInit(I2C_TypeDef* I2Cx);2.void I2C_Init(I2C_TypeDef* I2Cx, I2C_InitTypeDef* I2C_InitStruct);3.void I2C_StructInit(I2C_InitTypeDef* I2C_InitStruct);4.void I2C_Cmd(I2C_TypeDef* I2Cx, FunctionalState NewState);5.void I2C_DMACmd(I2C_Type…

BM57 岛屿数量(回溯)

对数组index的判断要放前面&#xff0c;要不然报数组越界异常。 import java.util.*;public class Solution {/*** 代码中的类名、方法名、参数名已经指定&#xff0c;请勿修改&#xff0c;直接返回方法规定的值即可** 判断岛屿数量* param grid char字符型二维数组 * return …

Unity开发者3D模型基础

术语“3D 建模”是指使用特殊软件创建对象或表面的 3D 数字表示的过程。 3D 模型可用于各种不同的目的&#xff0c;包括电影、视频游戏、建筑和工程。 3D 建模也是创建虚拟现实 (VR) 和增强现实 (AR) 体验工作的重要组成部分。 我们通常通过构建或获取 3D 模型并将其导入 Unit…

ssm028蜀都天香酒楼的网站设计与实现+jsp

基于JSP的蜀都天香酒楼管理系统的设计与实现 摘要 近年来&#xff0c;信息化管理行业的不断兴起&#xff0c;使得人们的日常生活越来越离不开计算机和互联网技术。首先&#xff0c;根据收集到的用户需求分析&#xff0c;对设计系统有一个初步的认识与了解&#xff0c;确定蜀都…

加速 Redis 操作:掌握管道技术提升性能与效率

Redis 管道技术是一种用于优化 Redis 命令执行效率的机制。在传统的 Redis 操作中&#xff0c;每次向 Redis 服务器发送一个命令&#xff0c;都需要等待命令执行完成并返回结果&#xff0c;这样会导致频繁的网络通信和服务器端的命令执行开销&#xff0c;降低系统的性能和吞吐量…

ssm026校园美食交流系统+vue

校园美食交流系统 摘 要 1 前 言 3 第1章 概述 4 1.1 研究背景 4 1.2 研究目的 4 1.3 研究内容 4 第二章 开发技术介绍 5 2.1Java技术 6 2.2 Mysql数据库 6 2.3 B/S结构 7 2.4 SSM框架 8 第三章 系统分析 9 3.1 可行性分析 9 3.1.1 技术可行性 9 3.1.2 经济可行…

极客时间: 用 Word2Vec, LangChain, Gemma 模拟全本地检索增强生成(RAG)

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

第7章 数据安全

思维导图 7.1 引言 数据安全包括安全策略和过程的规划、建立与执行&#xff0c;为数据和信息资产提供正确的身份验证、授权、访问和审计。虽然数据安全的详细情况(如哪些数据需要保护)因行业和国家有所不同&#xff0c;但是数据安全实践的目标是相同的&#xff0c;即根据隐私和…

在Qt中使用Python时报错, expected unqualified-id before ‘;‘ token

在Qt中使用Python时报错&#xff0c;主要原因是include了一个 <pybind11/embed.h>文件 //object.h PyType_Slot *slots; ----------------------报错信息---------------------- /usr/include/python3.8/object.h:190: error: expected unqualified-id before ; token19…

jQuery笔记 01

目录 01 jquery概述 02 jq的特点和基本操作 03 jq的过滤选择器 04 jq的筛选选择器 05 jq对象和js对象的相互转化 06 jq的css方法 07 jq的class方法 01 jquery概述 jq是一个第三方的库文件 是其他团队 把js一些繁琐的操作封装成了一个个的方法 放到一个单独的js文件里…

隐私计算实训营第七讲-隐语SCQL的开发实践

隐私计算实训营第七讲-隐语SCQL的开发实践 文章目录 隐私计算实训营第七讲-隐语SCQL的开发实践1.如何使用SCQL&#xff1f;2.使用流程3.SCQL部署4.SCQL使用示例4.1创建用户4.2创建项目&用户授权4.3创建表4.4设置CCL4.5发起联合分析查询 1.如何使用SCQL&#xff1f; 2.使用流…

Echarts实现高亮某一个点

背景 接口会返回所有点的数据&#xff0c;以及最优点的数据。产品要求在绘制图形后&#xff0c;高亮最优点&#xff0c;添加一个红色的样式&#xff0c;如图。点击select选择器时&#xff0c;可选择不同指标和花费对应的关系。 以下介绍实现思路 1、自定义配置选择器的数据源…

关于Fragment这一篇就够了

Fragment是什么&#xff1f; Fragment是Android中的一个组件&#xff0c;它被引入在Android 3.0&#xff08;API 11&#xff09;中。Fragment是依赖于Activity的&#xff0c;不能独立存在。一个Activity可以有多个Fragment&#xff0c;一个Fragment也可以被多个Activity重用。…

C#基础:类,对象,类成员简介(第四节课)

本节内容&#xff1a; 类与对象的关系 什么时候叫“对象”&#xff0c;什么时候叫实例引用变量与实例的关系 类的三大成员 属性方法事件 类的静态成员与实例成员 关于“绑定” 1.什么是类&#xff1a;&#xff08;再详细一点&#xff09; 类是对现实世界事物进行抽象所…

LLM是优秀的手语翻译者

LLM是优秀的手语翻译者 简介Related WorkMethodSignLLM Overviewector-Quantized Visual Sign ModuleCodebook Reconstruction and Alignment LLMs are Good Sign Language Translators 简介 基于观察&#xff0c;我们发现LLMs可以通过利用与之前学习过的语言的共有特性来有效…

CPU+GPU+NPU三位一体AI边缘控制器,三屏异显,搭载RK3588处理器

XMS-201采用了Rockchip RK3588八核64位处理器&#xff0c;集成ARM Mali-G610 MP4四核GPU&#xff0c;内置AI加速器NPU&#xff0c;可提供6Tops算力&#xff0c;支持主流的深度学习框架&#xff1b;性能强劲的RK3588可为各类AI应用场景带来更强大的性能表现&#xff0c;适用于机…

青蛙跳杯子【蓝桥杯】/bfs

青蛙跳杯子 bfs 思路&#xff1a;刚开始用的是dfs&#xff0c;但是不太行&#xff0c;DFS 可能会导致搜索深度过深&#xff0c;增加了时间复杂度&#xff0c;BFS 适合求解最短路径问题&#xff0c;BFS 在搜索过程中&#xff0c;首先访问距离初始节点最近的节点&#xff0c;因此…