javalock(八)ReentrantReadWriteLock
ReentrantReadWriteLock:
同时实现了共享锁和排它锁。内部有一个sync,同时实现了tryAcquire/tryReleases、tryAcquireShared/tryReleasesShared,一共四个函数,然后ReentrantReadWriteLock内部还实现了一个ReadLock和一个WriteLock,ReadLock和WriteLock都实现了lock和unlock函数,然后ReadLock和WriteLock是对同一个Sync对象的封装,不同之处在于ReadLock的lock函数调用的是Sync.tryAcquireShared,unlock调用的是tryReleasesShared,而WriteLock的lock函数调用的是Sync.tryAcquire/Sync.tryReleases。还有ReadLock重载了newCondition,直接抛异常,因为Condition会调用isHeldExclusive来判断当前线程是否拥有排它锁,而ReadLock是共享锁,这矛盾了,所以ReadLock重载了newCondition函数,然后直接抛异常,而WriteLock则可以正常newCondition,因为WriteLock是排它锁,所以可以支持condition,换句话说:只有排它锁才支持condition。
ReentrantReadWriteLock:
锁资源由两部分组成{state,Holder},state是一个int,用来记录已经获取读锁的线程数和已经获取写锁的线程数,state的32位字节分成了两部分,高16位表示已经获取读锁的线程数,低16位表示写者重入的次数,因为写锁是排它锁,也就是说只会有一个线程获取写锁,所以如果state低16位不为0就表示有人获得了写锁,然后这里就直接用低16位来记录写锁重入的个数,如果读者数不为0,则写者数必定为0,如果写者数为0则读者数必定为0,也就是说读写锁互斥。holder则是记录锁的获取信息,因为是reentrant即可冲入锁,也就是说可能出现这种情况:多个线程同时获取了读锁,然后多个线程又多次readLock.lock,所以就需要一个结构体来记录当前线程是否获得了读锁以及当前线程重入的次数,所以holder是一个threadlocal变量,每个线程都有一份,如果当前线程没有获得该锁,则删除该threadLocal,前面说了写锁重入次数直接用state低16位记录,并且用父类的owner来记录谁获得了写锁,所以写锁不用holder,holder只用于读锁。注意ReentrantLock不需要holder的原因是ReentrantLock是排它锁,最多只有一个线程能获得锁,而父类提供的state和owner变量就足够了,所以不需要holder变量。
一个线程已经获取写锁以后,可以继续获取读锁,但是反过来不行,一个线程获取了读锁后,是不允许再次获取写锁的,原因很简单,如果线程先获取了写锁,那么就能保证其他所有线程都不能获取读锁,所以线程可以安全获取读锁,因为只有他一个人能获取读写权限,而反过来如果线程先获取了读锁,那么就可能还有其他线程此时也获取了读锁,这样就不止一个线程获取了读锁,所以此时是不能获取写锁的。
import java.util.concurrent.TimeUnit;
import java.util.Collection;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;//同时实现了读写锁
public class ReentrantReadWriteLockimplements ReadWriteLock, java.io.Serializable {private static final long serialVersionUID = -6992448646407690164L;//读锁对象private final ReentrantReadWriteLock.ReadLock readerLock;//写锁对象private final ReentrantReadWriteLock.WriteLock writerLock;//读锁对象和写锁对象封装了同一个sync对象final Sync sync;public ReentrantReadWriteLock() {this(false);}//默认是非公平锁public ReentrantReadWriteLock(boolean fair) {sync = fair ? new FairSync() : new NonfairSync();//readLock和writeLock都是使用的同一个ReentrantReadWriteLock对象readerLock = new ReadLock(this);writerLock = new WriteLock(this);}public ReentrantReadWriteLock.WriteLock writeLock() { return writerLock; }public ReentrantReadWriteLock.ReadLock readLock() { return readerLock; }abstract static class Sync extends AbstractQueuedSynchronizer {private static final long serialVersionUID = 6317671515068378041L;//读者数偏移,读者数=state>>SHARED_SHIFTstatic final int SHARED_SHIFT = 16;//读者数+1就直接加SHARED_UNIT就行static final int SHARED_UNIT = (1 << SHARED_SHIFT);//最大读者数static final int MAX_COUNT = (1 << SHARED_SHIFT) - 1;//写者重入次数数掩码,写者数=state&&EXCLUSIVE_MASKstatic final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;static int sharedCount(int c) { return c >>> SHARED_SHIFT; }static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }//定义的holder类,记录了当前线程id,以及加读锁的次数//如果线程获得了读锁,就会拥有一个holdCounter对象//如果线程没有获取读锁,就会删除holdCounter对象//因为HoldCounter对象被用作ThreadLocal对象static final class HoldCounter {//记录读锁重入的次数int count = 0;//线程idfinal long tid = getThreadId(Thread.currentThread());}static final class ThreadLocalHoldCounterextends ThreadLocal<HoldCounter> {public HoldCounter initialValue() {return new HoldCounter();}}//这里解释一下readHolds/cachedHoldCounter/firstReader/firstReaderHoldCount//这些变量都只有一个目的:如果当前线程持有读锁,则返回本线程重入次数//readHolds是一个ThreadLocal变量,其他三个都不是ThreadLocal//本来要返回本线程重入次数直接返回ThreadLocal内保存的count就行了//但是ThreadLocal.get可能比较慢,所以为了优化,//就用了两个变量firstReader/cachedHolderCount//firstReader记录的是当没有任何获取读锁时,记录第一个获得读锁的线程的thread对象//cachedHolderCount记录的是上次获取读锁的线程的id//readHolds记录的是本线程的信息,是threadLocal//然后如果有人要获取当前线程读锁的重入次数,那么获取逻辑是这样的://先看能不能从firstReader读,如果firstReader就是当前线程//那么就直接返回firstReadHoldCount,就不用去读threadLocal了//如果firstReader不是当前线程,则尝试去cachedHolderCount读//如果上一次获取读锁的线程就是当前线程,那么ok,可以直接从cachedHolderCount读//这样就不用去读ThreadLocal了,如果都失败了,那么就只能去读readHolds了//而读threadLocal肯定是比较慢的。。。(花里胡哨的,不过文档说有助于提高并发效率。。。)private transient ThreadLocalHoldCounter readHolds;private transient HoldCounter cachedHoldCounter;private transient Thread firstReader = null;private transient int firstReaderHoldCount;Sync() {readHolds = new ThreadLocalHoldCounter();setState(getState()); }abstract boolean readerShouldBlock();abstract boolean writerShouldBlock();//释放写锁protected final boolean tryRelease(int releases) {//首先判断当前线程是否拥有锁:直接判断owner是不是当前线程,如果是则拥有if (!isHeldExclusively())throw new IllegalMonitorStateException();//然后扣减写锁计数,写锁是低16位,所以可以直接减int nextc = getState() - releases;//判断写锁重入计数是否为0boolean free = exclusiveCount(nextc) == 0;if (free)//如果是则设置owner为nullsetExclusiveOwnerThread(null);//然后设置state//笔记:在释放写锁前,读者数必定为0setState(nextc);return free;}//获取读锁protected final boolean tryAcquire(int acquires) {//获取当前线程Thread current = Thread.currentThread();//获取锁资源状态int c = getState();//计算写锁重入次数个数int w = exclusiveCount(c);//如果c!=0,表示有人获得了读锁或者有人获得了写锁if (c != 0) {//c!=0,但是w=0,表明有人获得了读锁//c!=0,并且w!=0,表示有人获得了写锁,所以还需要判断是不是自己获得了写锁//所以这里就是如果有人获得了读锁或者获取写锁的线程不是自己,那么本次写锁获取失败if (w == 0 || current != getExclusiveOwnerThread())return false;//如果是自己获得了写锁,则判断可重入次数是否超过了(2^16-1)次if (w + exclusiveCount(acquires) > MAX_COUNT)throw new Error("Maximum lock count exceeded");//如果没有,则表示成功获取锁资源,返回true//注意:此时是线程已经获得了锁,并且是自己,// 也就是说只有本线程可以修改state,所以此时无需用cas操作setState(c + acquires);return true;}//如果c==0表示此时没有任何人获得锁//因为ReentrantReadWrite支持公平或者非公平锁,所以writeShouldBlock是一个抽象方法//公平方式:看aqs的sync list是否有节点等待,如果有则返回true,表示当前线程不能获取锁//非公平方式:writeShouldBlock直接返回false,表示立即尝试获取锁if (writerShouldBlock() ||//如果不需要阻塞,则立即通过cas尝试获取锁!compareAndSetState(c, c + acquires))//如果cas获取失败就返回false表示本次获取锁失败return false;//获取成功就设置锁拥有者为自己setExclusiveOwnerThread(current);return true;}//释放读锁protected final boolean tryReleaseShared(int unused) {Thread current = Thread.currentThread();//此处到下面for循环前面的都是为了更新当前线程的读锁重入次数//如果当前线程就是自己,那么直接更新firstReaderHoldCount//就不用去写threadLocal了if (firstReader == current) {//如果重入次数为1,那么-1之后就表示释放掉了//所以直接把firstReader设置为null//这样,firstReader是无效的,那么firstReaderHoldCount就肯定失效了//所以此处只设置firstReader=null,而没有扣减firstReaderHoldCountif (firstReaderHoldCount == 1)firstReader = null;else//否则读锁重入次数大于1,释放一次后,线程还是拥有读锁//所以firstReader不用变firstReaderHoldCount--;} else {//如果自己不是firstReader,那么再尝试cachedHoldCounter//即尝试自己是不是上一次访问的线程HoldCounter rh = cachedHoldCounter;//如果缓存的holder为null或者缓存的holder不是当前线程的if (rh == null || rh.tid != getThreadId(current))//那么就表示从cachedHoldCounter读取失败//此时就只能去读threadLocal了rh = readHolds.get();//此时rh为cachedHoldCounter或者readHoldsint count = rh.count;//如果count<=1,那么释放一次后就为0//所以此时就要移除threadLocal//免得内存泄漏,比如一个线程获取了读锁,释放后就永不再是用这个rw锁了if (count <= 1) {//移除threadLocalreadHolds.remove();if (count <= 0)//如果count<=0就表示自己没有持有读锁,却常是释放读锁throw unmatchedUnlockException();}//当前线程重入次数-1--rh.count;}//更新完读锁重入次数,那么下面就是通过cas更新state也就是更新读锁总数for (;;) {//获取读锁总次数int c = getState();//-1int nextc = c - SHARED_UNIT;//cas设置stateif (compareAndSetState(c, nextc))return nextc == 0;}}private IllegalMonitorStateException unmatchedUnlockException() {return new IllegalMonitorStateException("attempt to unlock read lock, not locked by current thread");}//尝试获取读锁//!!!在持有写锁的状态下允许获取读锁,但是反过来持有读锁时是不允许再次获取写锁//也就是说支持锁降级(当然,这里没有降级),但是不支持锁升级protected final int tryAcquireShared(int unused) {//获取当前线程对象Thread current = Thread.currentThread();//获取锁资源状态int c = getState();//判断是否有线程持有写锁,如果有,则判断是不是当前线程if (exclusiveCount(c) != 0 &&getExclusiveOwnerThread() != current)//如果不是,则返回-1表剩余读锁资源不足,读锁获取失败return -1;//走到此处,要么无人获取写锁,要么自己获取了写锁//先获取读者计数int r = sharedCount(c);//判断本次获取读锁是否应该阻塞,分公平和非公平方式//非公平方式:如果aqs队列中第一个等待的线程是要获取写锁,那么本次读者应该等待//也就是避免写进程长时间获取不到读锁,也就是说如果第一个等待的线程是要获取读锁,//那么本线程会立即获取读锁,不管后面的,也就是说对读不公平,但是对写带点公平//公平方式:如果aqs队列中有等待的进程,则本次获取read需要阻塞// 可能是因为达到总读锁上限了,因为state用于读者锁计数的只有16位if (!readerShouldBlock() &&//如果没有达到总读者锁重入计数r < MAX_COUNT &&//那么就尝试获取读锁compareAndSetState(c, c + SHARED_UNIT)) {//下面就是更新线程对应的读锁重入次数了//r==0表示本线程是第一个获得读锁的线程if (r == 0) {//那么就设置firstReader为本线程firstReader = current;//并且读锁重入次数为1firstReaderHoldCount = 1;//如果当前线程正好是第一个读者} else if (firstReader == current) {//那么直接更新firstReaderHoldCount就行firstReaderHoldCount++;} else {//反之判断cachedHoldCounter是否为当前线程HoldCounter rh = cachedHoldCounter;//如果cached为null或者cached不是当前线程if (rh == null || rh.tid != getThreadId(current))//则更新cached为当前线程的readHoldcachedHoldCounter = rh = readHolds.get();//如果cached是自己,如果count=0表示我们之前已经删除了//那么直接设置readHolds等于我们缓存的,就不用重新创建了else if (rh.count == 0)readHolds.set(rh);//更新本线程读锁重入次数rh.count++;}//返回获取锁成功return 1;}//如果获取锁失败或者本线程获取读锁需要阻塞,则走重逻辑获取锁//重逻辑和上面的逻辑几乎一样return fullTryAcquireShared(current);}//while cas获取读锁final int fullTryAcquireShared(Thread current) {HoldCounter rh = null;for (;;) {//获取锁状态int c = getState();//判断是不是有人获取了写锁if (exclusiveCount(c) != 0) {//如果有,则再判断是不是自己if (getExclusiveOwnerThread() != current)//如果不是,则返回失败return -1;//如果本次读者需要阻塞://非公平方式:如果aqs队列中第一个等待的线程是要获取写锁,那么本次读者应该等待//也就是避免写进程长时间获取不到读锁,也就是说如果第一个等待的线程是要获取读锁,//那么本线程会立即获取读锁,不管后面的,也就是说对读不公平,但是对写带点公平//公平方式:如果aqs队列中有等待的进程,则本次获取read需要阻塞// 可能是因为达到总读锁上限了,因为state用于读者锁计数的只有16位} else if (readerShouldBlock()) {//这里又是一样的逻辑,用来更新count的,懒得写了if (firstReader == current) {} else {if (rh == null) {rh = cachedHoldCounter;if (rh == null || rh.tid != getThreadId(current)) {rh = readHolds.get();if (rh.count == 0)readHolds.remove();}}if (rh.count == 0)return -1;}}if (sharedCount(c) == MAX_COUNT)throw new Error("Maximum lock count exceeded");//cas操作尝试获取锁if (compareAndSetState(c, c + SHARED_UNIT)) {//下面又是更新count的逻辑了,和上面一样,略if (sharedCount(c) == 0) {firstReader = current;firstReaderHoldCount = 1;} else if (firstReader == current) {firstReaderHoldCount++;} else {if (rh == null)rh = cachedHoldCounter;if (rh == null || rh.tid != getThreadId(current))rh = readHolds.get();else if (rh.count == 0)readHolds.set(rh);rh.count++;cachedHoldCounter = rh; }//返回1表示读锁资源剩余1//这里是固定返回1,就是说不管获取多少次读锁,剩余读锁资源总是1return 1;}}}//和tryAcquire逻辑一模一样,唯一区别就是这里即使线程拥有写锁,也是通过cas更新state//略final boolean tryWriteLock() {Thread current = Thread.currentThread();int c = getState();if (c != 0) {int w = exclusiveCount(c);if (w == 0 || current != getExclusiveOwnerThread())return false;if (w == MAX_COUNT)throw new Error("Maximum lock count exceeded");}if (!compareAndSetState(c, c + 1))return false;setExclusiveOwnerThread(current);return true;}//和fullTryAcquireShared逻辑几乎一模一样,略final boolean tryReadLock() {Thread current = Thread.currentThread();for (;;) {int c = getState();if (exclusiveCount(c) != 0 &&getExclusiveOwnerThread() != current)return false;int r = sharedCount(c);if (r == MAX_COUNT)throw new Error("Maximum lock count exceeded");if (compareAndSetState(c, c + SHARED_UNIT)) {if (r == 0) {firstReader = current;firstReaderHoldCount = 1;} else if (firstReader == current) {firstReaderHoldCount++;} else {HoldCounter rh = cachedHoldCounter;if (rh == null || rh.tid != getThreadId(current))cachedHoldCounter = rh = readHolds.get();else if (rh.count == 0)readHolds.set(rh);rh.count++;}return true;}}}//判断当前线程是否拥有写锁protected final boolean isHeldExclusively() {return getExclusiveOwnerThread() == Thread.currentThread();}//写锁支持条件变量,writeLock的newCondition中调用这个函数//注意:读锁是不支持的,readLock的newCondition函数则是直接跑UnSupport异常final ConditionObject newCondition() {return new ConditionObject();}//如果写锁已分配,获取持有写锁的线程对象final Thread getOwner() {return ((exclusiveCount(getState()) == 0) ?null :getExclusiveOwnerThread());}//获取读锁总次数final int getReadLockCount() {return sharedCount(getState());}//判断是否有现成是否已经获得了写锁final boolean isWriteLocked() {return exclusiveCount(getState()) != 0;}//获取写锁重入次数,就是state低15位final int getWriteHoldCount() {return isHeldExclusively() ? exclusiveCount(getState()) : 0;}//获取当前线程读锁重入次数final int getReadHoldCount() {if (getReadLockCount() == 0)return 0;Thread current = Thread.currentThread();//先尝试从firstReader读取,if (firstReader == current)return firstReaderHoldCount;//如果firstReader不是自己,则再尝试从cachedHolderCount读取HoldCounter rh = cachedHoldCounter;if (rh != null && rh.tid == getThreadId(current))return rh.count;//如果cachedHolderCount也不是自己,那么最后才去读threadLocal,即读readHolderint count = readHolds.get().count;if (count == 0) readHolds.remove();return count;}private void readObject(java.io.ObjectInputStream s)throws java.io.IOException, ClassNotFoundException {s.defaultReadObject();readHolds = new ThreadLocalHoldCounter();setState(0); }final int getCount() { return getState(); }}//非公平锁static final class NonfairSync extends Sync {private static final long serialVersionUID = -8159625535654395037L;//本次获取写锁是否该阻塞?答案是:用不阻塞final boolean writerShouldBlock() {return false; }//本次获取读锁是否应该阻塞?//答案是如果是如果第一个等待的线程是写锁,则本次获取读锁需要阻塞//反之如果是读锁,则不阻塞,对读锁不公平,对写锁稍显公平final boolean readerShouldBlock() {return apparentlyFirstQueuedIsExclusives();}}//公平方式static final class FairSync extends Sync {private static final long serialVersionUID = -2274990926593161451L;//本次获取写锁是否需要阻塞?如果等待队列不为空则需要阻塞final boolean writerShouldBlock() {return hasQueuedPredecessors();}//本次获取读锁是否需要阻塞?如果等待队列不为空则需要阻塞final boolean readerShouldBlock() {return hasQueuedPredecessors();}}//下面就是ReadLock和WriteLock了,这两都是对上面的Sync的一个简单封装//ReadLock和WriteLock的各种函数都是简单的转调用Sync的相关函数,//一眼就能看明白,略public static class ReadLock implements Lock, java.io.Serializable {private static final long serialVersionUID = -5992448646407690164L;private final Sync sync;protected ReadLock(ReentrantReadWriteLock lock) {sync = lock.sync;}public void lock() {sync.acquireShared(1);}public void lockInterruptibly() throws InterruptedException {sync.acquireSharedInterruptibly(1);}public boolean tryLock() {return sync.tryReadLock();}public boolean tryLock(long timeout, TimeUnit unit)throws InterruptedException {return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));}public void unlock() {sync.releaseShared(1);}//!!!读锁不支持条件变量public Condition newCondition() {throw new UnsupportedOperationException();}public String toString() {int r = sync.getReadLockCount();return super.toString() +"[Read locks = " + r + "]";}}public static class WriteLock implements Lock, java.io.Serializable {private static final long serialVersionUID = -4992448646407690164L;private final Sync sync;protected WriteLock(ReentrantReadWriteLock lock) {sync = lock.sync;}public void lock() {sync.acquire(1);}public void lockInterruptibly() throws InterruptedException {sync.acquireInterruptibly(1);}public boolean tryLock( ) {return sync.tryWriteLock();}public boolean tryLock(long timeout, TimeUnit unit)throws InterruptedException {return sync.tryAcquireNanos(1, unit.toNanos(timeout));}public void unlock() {sync.release(1);}//写锁支持条件变量,就是AQS的条件变量public Condition newCondition() {return sync.newCondition();}public String toString() {Thread o = sync.getOwner();return super.toString() + ((o == null) ?"[Unlocked]" :"[Locked by thread " + o.getName() + "]");}public boolean isHeldByCurrentThread() {return sync.isHeldExclusively();}public int getHoldCount() {return sync.getWriteHoldCount();}}
相关文章:
javalock(八)ReentrantReadWriteLock
ReentrantReadWriteLock: 同时实现了共享锁和排它锁。内部有一个sync,同时实现了tryAcquire/tryReleases、tryAcquireShared/tryReleasesShared,一共四个函数,然后ReentrantReadWriteLock内部还实现了一个ReadLock和一个WriteLock,…...
反射和设计模式
一、反射 1. 相关概念 (1) 类的对象:基于定义好的一个类,创建该类的实例,即利用 new 创建的实例就为类的对象。 (2) 类对象:类加载的产物,封装了一个类的所有信息 ( 包名、类名、父类、接口、属性、方法、构造方…...

双指针---和为s的两个数字
这里写自定义目录标题 题目链接问题分析代码解决执行用时 题目链接 购物车内的商品价格按照升序记录于数组 price。请在购物车中找到两个商品的价格总和刚好是 target。若存在多种情况,返回任一结果即可。 问题分析 暴⼒解法,会超时 (两层…...

LLaMA-Factory 单卡3080*2 deepspeed zero3 微调Qwen2.5-7B-Instruct
环境安装 git clone https://gitcode.com/gh_mirrors/ll/LLaMA-Factory.git 下载模型 pip install modelscope modelscope download --model Qwen/Qwen2.5-7B-Instruct --local_dir /root/autodl-tmp/models/Qwen/Qwen2.5-7B-Instruct 微调 llamafactory-cli train \--st…...

智慧农业云平台与水肥一体化:道品科技引领农业现代化新潮流
在当今科技飞速发展的时代,农业也正经历着一场深刻的变革。智慧农业云平台和水肥一体化技术的出现,为农业生产带来了前所未有的机遇和挑战。 一、智慧农业云平台:农业生产的 “智慧大脑” 智慧农业云平台就像是农业生产的 “智慧大脑”&…...

241207_MindNLP中的大模型微调
241207_基于MindNLP的大模型高效微调 现在的大模型体量非常庞大,全量微调所需要的算力也特别庞大,个人开发者没有条件微调。参数量达到7B的模型才刚刚有涌现能力,但是我们要微调7B的模型的话,就需要328G的显存,至少需…...

MongoDB、Mongoose使用教程
文章目录 一:MongoDB 简介1.1 什么是 MongoDB1.2 特点1.3 与关系数据库的区别:1.4 资源链接: 二:安装 MongoDB2.1 安装前的准备2.2 安装、启动 MongoDB2.3 创建用户 MongoDB 三、连接四:MongoDB 基础操作4.1 库操作&am…...
单片机:实现控制步进电机正反转(附带源码)
1. 步进电机概述 步进电机(Step Motor)是一种能够将电能转换为机械能的电动机。其独特之处在于能够精确地控制转动角度,因此被广泛应用于需要精确控制的场合,如打印机、机器人、数控机床、自动化设备等。 步进电机的转动是以“步…...

安装指南|OpenCSG Starship上架GitHub Marketplace
在代码开发的日常中,你是否常常被以下问题困扰? 代码审查耗时太长,拖慢项目进度? 审查质量参差不齐,一些关键问题被遗漏? 复杂代码变更看不懂,审查者需要大量时间理解意图? 别担…...

Excel设置生日自动智能提醒,公式可直接套用!
大家好,我是小鱼。 今天跟大家分享一个WPS表格中根据出生日期,设置生日提醒,并且根据距离生日天数自动标记数据颜色。简单又实用,一个公式轻松搞定! 接下来我们先学习一下需要使用到的函数,然后再根据实例让…...

同步异步日志系统:前置知识
一、日志项目的介绍 1.1 为什么要有日志系统 1、⽣产环境的产品为了保证其稳定性及安全性是不允许开发⼈员附加调试器去排查问题,可以借助日志系统来打印⼀些⽇志帮助开发⼈员解决问题 为什么不直接printf打印在屏幕上呢??因为现实中没有…...
微服务设计原则——功能设计
文章目录 1.ID生成2.数值精度3.DB操作4.性能测试5.版本兼容5.1 向旧兼容5.2 向新兼容 6.异步时序问题7.并发问题7.1 并发时序7.2 并发数据竞争 参考文献 1.ID生成 在分布式系统中,生成全局唯一ID是非常重要的需求,因为需要确保不同节点、服务或实例在并…...

低代码软件搭建自学的第一天——熟悉PyQt
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 学习计划第 1 步:环境搭建1.1 安装 Python1.2 安装 PyQt安装命令:验证安装: 第 2 步:PyQt 基础知识2.1 创建第一个窗…...

基于Python3编写的Golang程序多平台交叉编译自动化脚本
import argparse import os import shutil import sys from shutil import copy2from loguru import loggerclass GoBuild:"""一个用于构建跨平台执行文件的类。初始化函数,设置构建的主文件、生成的执行文件名称以及目标平台。:param f: 需要构建的…...

远程桌面连接
电脑A:使用机 电脑B:被控制的另一个 方法1: 在电脑B上操作 ①winr输入cmd进入命令行窗口,输入ipconfig查询本机地址 ②我的电脑/此电脑 右键点击“属性” ③选择屏幕右边“远程桌面” ④打开“启用远程桌面” ⑤打开设置&am…...
网络地址转换NAT
NAT(Network Address Translation) 方法于1994年提出。需要在专用网连接到因特网的路由器上安装NAT软件。装有NAT软件的路由器叫做NAT路由器,它至少有一个有效的外部全球地址IPG。 所有使用本地地址的主机在和外界通信时都要在NAT路由器上将其本地地址转换成外部全球…...

什么是CRM管理软件?CRM的基本概念、功能、选择标准、应用场景
什么是CRM管理软件? 嘿,大家好!今天咱们聊聊一个在现代企业管理中非常重要的工具——CRM管理软件。CRM是Customer Relationship Management(客户关系管理)的缩写,简单来说,它就是一个帮助企业和…...

Python编程常用的19个经典案例
Python 的简洁和强大使其成为许多开发者的首选语言。本文将介绍36个常用的Python经典代码案例。这些示例覆盖了基础语法、常见任务、以及一些高级功能。 1. 列表推导式 fizz_buzz_list ["FizzBuzz" if i % 15 0 else "Fizz" if i % 3 0 else "Buzz…...

【Unity基础】AudioSource 常用方法总结
在 Unity 中,AudioSource 组件用于控制音频的播放和管理。以下是常用的 AudioSource 控制方法及其说明。 1. 播放和暂停音频 Play():开始播放音频,如果是从暂停的地方继续播放,可以直接调用。Pause():暂停当前播放的…...
CSS系列(25)-- 滚动优化详解
前端技术探索系列:CSS 滚动优化详解 📜 致读者:探索流畅滚动的艺术 👋 前端开发者们, 今天我们将深入探讨 CSS 滚动优化,学习如何创建流畅、高性能的滚动体验。 平滑滚动 🚀 基础设置 /* …...

RocketMQ延迟消息机制
两种延迟消息 RocketMQ中提供了两种延迟消息机制 指定固定的延迟级别 通过在Message中设定一个MessageDelayLevel参数,对应18个预设的延迟级别指定时间点的延迟级别 通过在Message中设定一个DeliverTimeMS指定一个Long类型表示的具体时间点。到了时间点后…...

关于iview组件中使用 table , 绑定序号分页后序号从1开始的解决方案
问题描述:iview使用table 中type: "index",分页之后 ,索引还是从1开始,试过绑定后台返回数据的id, 这种方法可行,就是后台返回数据的每个页面id都不完全是按照从1开始的升序,因此百度了下,找到了…...
Auto-Coder使用GPT-4o完成:在用TabPFN这个模型构建一个预测未来3天涨跌的分类任务
通过akshare库,获取股票数据,并生成TabPFN这个模型 可以识别、处理的格式,写一个完整的预处理示例,并构建一个预测未来 3 天股价涨跌的分类任务 用TabPFN这个模型构建一个预测未来 3 天股价涨跌的分类任务,进行预测并输…...

对WWDC 2025 Keynote 内容的预测
借助我们以往对苹果公司发展路径的深入研究经验,以及大语言模型的分析能力,我们系统梳理了多年来苹果 WWDC 主题演讲的规律。在 WWDC 2025 即将揭幕之际,我们让 ChatGPT 对今年的 Keynote 内容进行了一个初步预测,聊作存档。等到明…...
Java多线程实现之Callable接口深度解析
Java多线程实现之Callable接口深度解析 一、Callable接口概述1.1 接口定义1.2 与Runnable接口的对比1.3 Future接口与FutureTask类 二、Callable接口的基本使用方法2.1 传统方式实现Callable接口2.2 使用Lambda表达式简化Callable实现2.3 使用FutureTask类执行Callable任务 三、…...

Cinnamon修改面板小工具图标
Cinnamon开始菜单-CSDN博客 设置模块都是做好的,比GNOME简单得多! 在 applet.js 里增加 const Settings imports.ui.settings;this.settings new Settings.AppletSettings(this, HTYMenusonichy, instance_id); this.settings.bind(menu-icon, menu…...
解决本地部署 SmolVLM2 大语言模型运行 flash-attn 报错
出现的问题 安装 flash-attn 会一直卡在 build 那一步或者运行报错 解决办法 是因为你安装的 flash-attn 版本没有对应上,所以报错,到 https://github.com/Dao-AILab/flash-attention/releases 下载对应版本,cu、torch、cp 的版本一定要对…...

【Oracle】分区表
个人主页:Guiat 归属专栏:Oracle 文章目录 1. 分区表基础概述1.1 分区表的概念与优势1.2 分区类型概览1.3 分区表的工作原理 2. 范围分区 (RANGE Partitioning)2.1 基础范围分区2.1.1 按日期范围分区2.1.2 按数值范围分区 2.2 间隔分区 (INTERVAL Partit…...
PAN/FPN
import torch import torch.nn as nn import torch.nn.functional as F import mathclass LowResQueryHighResKVAttention(nn.Module):"""方案 1: 低分辨率特征 (Query) 查询高分辨率特征 (Key, Value).输出分辨率与低分辨率输入相同。"""def __…...
A2A JS SDK 完整教程:快速入门指南
目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库ÿ…...