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

JUC 学习笔记

并发与并行
并发:多个线程轮流使用CPU执行任务,将CPU的时间分割成合适大小的时间片,每一个线程拿到时间片之后就会利用CPU资源执行任务,当时间到了之后就会把CPU资源释放,并且保存线程的上下文,比如程序计数器,栈帧等资源。由于CPU执行速度很快,所以用户肉看看不出程序的停顿 ,看起来是多个事务同时运行。
并行:本来就是一批线程运行在一个CPU核上,另一批运行在另一个核上,他们之间不需要抢夺CPU资源,天然的并行。
线程与线程池的状态
线程的状态
(1)初始化:创建线程
(2)运行:
就绪状态:线程已经准备好,还没有拿到CPU时间片。例如:线程调用start()、线程sleep()结束、线程时间片用完、线程拿到对象锁等。
运行中状态:已经拿到CPU时间片,开始运行
(3)阻塞BLOCKED:等待阻塞、同步阻塞、其他阻塞,例如等待锁资源释放的时候会进入阻塞状态,自旋一段时间如果获取不到锁就释放CPU,状态由用户态转为内核态,由内核统一维护一个entry list存放阻塞的线程。
(4)等待WAITING:例如执行wait方法,底层调用的是unsafe.park(),执行wait方法的线程会被挂起,自动释放CPU和锁资源,状态从用户态转为内核态,由内核统一维护一个wait set存放需要等待通知的线程,WAITING需要显式的被唤醒。当调用notify方法会从wait set里面挑一个线程放入entry list,然后从entry list里挑一个线程唤醒去抢锁。
(5)超时等待:例如执行sleep(time)/wait(time),不会释放锁资源,执行wait(time),到达时间自动唤醒
(6)终止:线程run方法结束之后就终止了,虽然它可能是活的,但是不能继续执行任务,因为线程一旦终止就不能复生。
2.2. BLOCKED VS WAITING状态
BLOCKED状态针对的是线程竞争锁失败而设定的;WAITING状态针对的时线程条件等待唤醒而设定的。这两个状态首先场景不同,而且两种状态的设定可以让排查问题的时候更方便定位,一种是锁饥饿,一种是业务等待。
线程池状态
(1)RUNNING:正在运行,可以接受新任务。其中核心线程会阻塞式地获取任务,因此不会进入终止状态
(2)SHUTDOWN:调用shutdown(),从RUNNING->SHUTDOWN,不接受新任务,只处理任务队列中的任务
(3)STOP:调用shutdownNow(),从RUNNING/SHUTDOWN - > STOP,不接受新任务也不处理现有任务
(4)TIDYING:所有任务都已经执行完毕,并且活跃线程数为0。SHUTDOWN/STOP在任务都执行完的情况下进入TIDYING。之后会调用terminated()方法
(5)TREMINATED:TIDYING状态下执行terminated()进入终止状态u。
多线程主要解决什么问题?
(1)首先它是一种池化的概念,可以降低线程创建、销毁带来的开销,提升任务处理的响应速度。例如 Web 容器建立成千上万的 HTTP 连接。
(1)提升吞吐量(单位时间干更多活):券平台在过滤券配置时并行过滤。
(2)降低接口延时:对于密集计算用线程池并行,监控日志上报单独开线程池批量定时发送降低主接口的延时。
(3)
start run wait sleep notify notifyAll的作用
start vs run:
start:
启动线程执行,此时线程出于就绪状态,等待CPU时间片,拿时间片之后就执行run方法。这就是真正启动线程了,各个线程之间是异步的效果
run:
就是线程类的普通方法,是线程体,如果使用Thread.run(),那它依然是主线程去执行这个run方法,开再多线程也是同步执行
wait vs sleep:
所属类不同:wait属于Object、sleep属于Thread
使用场景不同:wait必须在synchronized同步代码块内执行否则抛异常,sleep可以任意处执行。
释放锁资源不同:wait会释放锁,sleep不会
唤醒方式不同:wait方法需要通过notify、notifyAll唤醒,sleep可以主动唤醒。
线程进入状态不同:调用wait方法进入WAITING状态,调用sleep方法进入TIME_WAITING状态
notify:
属于Object的,执行notify方法会随机唤醒一个线程进入争夺队列取争夺锁资源
notifyAll
唤醒所有线程。
线程池与线程的创建方法
线程的创建方法:
继承Thread类、实现Runnable接口、实现Callable接口、线程池ExecutorService
线程池的创建方法:
Executors:线程池工具类(一般不用),创建固定的线程池:
newFixedThreadPool(线程数目):请求队列无界,可能会产生OOM
newChachedThreadPool:线程数量无界,可能会OOM
newSingleThreadPool:请求队列无界,可能会产生OOM
newScheduledThreadPool:线程数量无界,可能会OOM
TreadPoolExecutors:自定义线程池
答:主要的配置有四点:核心线程数、最大线程数、工作队列和拒绝策略
核心线程数和最大线程数和CPU核数、任务类型有关,对于CPU密集型和IO密集型,配置是不一样的。
有一种公式:CPU密集型:N、N+1,IO密集型:N、2N+1

另一种公式:线程数=
IO密集型任务等待时间很长,这就意味着可以给更多的线程,在IO阻塞的时候处理别的就绪的IO
CPU密集型任务大部分时间都在做计算,给过多线程用处不大,还会增加上下文切换的开销
但是!实际上一般会先按照公式设一个初值,根据可接受的接口响应耗时来进行压测,根据压测情况调整
工作队列:SynchronousQueue(同步队列)、ArrayBlockingQueue(有界队列)、LinkedBlockingQueue(无界队列)、PriorityQueue(优先级队列)
拒绝策略:AbortPolicy(丢弃并抛异常),discardPolicy(直接丢弃),discardOldestPolicy(丢弃最早进入队列的,然后把这次任务加入队列),CallersRunPolicy(谁调用谁执行,除非线程池shutdown,则丢弃任务)。
线程池状态:
Running:接收新任务
shutdown:不接受新任务,处理现有任务
stop:不接受新任务也,中断现有的任务,直接停止工作
tidying:如果所有任务都已终止,并且有效线程数(workerCount)为0,然后调用terminated()钩子u方法进入terminated状态。
terminated:彻底终止
线程池创建之后,里面就有线程吗?
没有,但是可以进行线程池的预热,调用prestartAllCoreThreads(预热所有核心线程)、prestartCoreThreads(预热一个线程)
核心线程数会被回收吗?
核心线程默认不会被回收,可以调用allowCoreThreadTimeOut 设置为true。

五、线程安全的三大要素
答:
原子性:一个操作或者多个操作要么全部执行,要么都不执行,执行的过程中不会被打断。
例如:synchronized(monitorenter、monitorexit底层也是使用内存屏障实现的)、原子类、值赋值、引用的赋值。
可见性:多线程访问同一个变量时,一个线程修改了变量值,其他线程立即能看到
synchronized与lock(释放锁时会将变量值刷回主内存)、volatile(强制刷回主内存)
有序性:线程执行的顺序。程序编译时处理器和编译器会进行指令重排,指令重排对单线程没有影响,但是会影响多线程。
synchronized和lock、volatile、happens-before原则(如果满足,则可以指令重排)
六、线程调度
Linux:进程调度线程。调度策略:分时调度、先到先服务、时间片轮转
Windows:基于优先级和抢占调度算法调度线程
Java线程调度:协同式调度(线程自己决定执行多久,执行完了才会切换)、抢占式调度(操作系统分配执行时间)
七、多线程异步编程
对于线程执行结果有组合关系的时候可以使用ContDownLatch、CyclicBarrier、CompletableFuture
ContDownLatch:
CyclicBarrier:多个线程在一个屏障处等待,latch扣减为0之后会继续回复原始数值,进行下一轮循环。
public static void main(){
CyclicBarrier latch = new CyclicBarrier(2);
Thread t1 = new Thread(MyThread);
t1.start();
// 主线程执行到这里等待 latch被扣减为0再继续执行。,t1的run方法里还有finally代码块会再执行一次latch.await()
latch.await();
Thread t2 = new Thread(MyThread);
t1.start();
// 主线程执行到这里等待 latch被扣减为0,再继续执行。
latch.await();
Thread t2 = new Thread(MyThread);
t1.start();
// 主线程执行到这里等待 latch被扣减为0,再继续执行。
latch.await();

}
public void MyThread implements Runnable{
private CyclicBarrier cb;
public MyBarrier(CyclicBarrier cb){
this.cb=cb;
}
public void run(){
try{
System.out.println(Thread.currentThread.getName());
}catch{

    }finally{cb.await();}
}

}
Future:
实现:
FutureTask task = new FutureTask<>(new Callable(){
@Override
public UserInfo call(){
return User.getinfo();
}
});
executeService.submit(task);

UserInfo userinfo = task.get(); // 阻塞式获取结果
UserInfo userinfo = task.isDone(); // 轮询式获取结果
然而阻塞获取或者轮询获取都与异步编程相违背。
CompletableFuture:
实现:
CompletableFuture task = CompletableFuture.supplyAsync(()->User,getinfo());
UserInfo userinfo = task.get(2,TimeUnit.SECONDS);
UserInfo userinfo = task.get();
如果不用单独创建线程池,默认使用ForkJoinPool线程池
supplyAsync方法执行异步任务支持返回值
runAsync方法执行异步任务不支持返回值
get() 和 join() 方法都会阻塞式返回异步调用的结果,但是get会显式抛异常,join会打包异常返回。
任务回调:
一元依赖:一个completableFuture依赖于前一个CF任务的实现
thenRun():执行完第一个任务之后再执行第二个任务,二者之间没有参数传递,没有返回值
thenRunAsync():如果执行第一个任务传入了自定义的线程池,那么thenRun执行第二个任务的时候还会用这个线程池,但是thenRunAsync()执行第二个任务不会用自定义线程池,而是用ForkJoin线程池。
thenAccept():将前一个Future任务执行完返回的结果作为本次任务的入参,传入回调方法中执行,但是回调方法没有返回值。
public class FutureThenAcceptTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
CompletableFuture orgFuture = CompletableFuture.supplyAsync(
()->{
System.out.println("原始CompletableFuture方法任务");
return "捡田螺的小男孩";
}
);
CompletableFuture thenAcceptFuture = orgFuture.thenAccept((a) -> {
if ("捡田螺的小男孩".equals(a)) {
System.out.println("关注了");
}
System.out.println("先考虑考虑");
});
System.out.println(thenAcceptFuture.get());
}
}
thenApply():将前一个Future任务执行完返回的结果作为本次任务的入参,传入回调方法中执行,但是回调方法有返回值。
public class FutureThenAcceptTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
CompletableFuture orgFuture = CompletableFuture.supplyAsync(
()->{
System.out.println("原始CompletableFuture方法任务");
return "捡田螺的小男孩";
}
);
CompletableFuture thenAcceptFuture = orgFuture.thenApply((a) -> {
if ("捡田螺的小男孩".equals(a)) {
System.out.println("关注了");
}
System.out.println("先考虑考虑");
});
System.out.println(thenApplyFuture.get());
}
二元依赖:一个CF任务依赖于两个CF任务
thenCombine():
CompletableFuture cf4 = cf1.thenCombine(cf2, (result1, result2) ->
{ //result1和result2分别为cf1和cf2的结果
return"result4";
});
多元依赖:一个CF任务依赖于多个CF任务
AllOf():
CompletableFuture cf6 = CompletableFuture.allOf(cf3, cf4, cf5);
CompletableFuture result = cf6.thenApply(v -> {
//这里的join并不会阻塞,因为传给thenApply的函数是在CF3、CF4、CF5全部完成时,才会执行 。
result3 = cf3.join();
result4 = cf4.join();
result5 = cf5.join();
//根据result3、result4、result5组装最终result;
return"result";
});
CF的原理:
一个CF对象里面有两个字段:result、stack(Completion链)。result存储的是当前CF任务执行完的结果。stack存储的是CF完成之后触发的动作,这些动作一个一个连在一起形成Completion链。
这里用到了观察者模式。当前CF任务是被观察者,后续依赖它的CF任务是观察者。
对于thenApply方法:
CF2依赖于CF1执行结束。
CF2=CF1.thenApply(()->{return ...}),此时CF1是被观察者、CF2是观察者
thenApply会接收一个函数式参数并把它封装到观察者的实现类UniApply(Completion的实现类)的fn里面作为一个回调函数,然后把UniApply注册到到CF1,也就是说压栈到CF1的stack里面。等CF1执行完毕之后会弹栈,执行回调函数。
对于thenCombine方法:
CF3依赖于CF2和CF1。
CF3=CF1.thenCombine(CF2,()->{ return .....})
这个时候就会把观察者的实现类为观察者的实现类为分别压栈到CF1和CF2里面。CF3会设置两个属性关联被依赖的CF1和CF2。
BiApply会检查依赖的两个CF是否都完成了,使用CAS设置状态避免重复触发。
对于AllOf:
CFn依赖于CF1、CF2...
CF4=CompletableFuture.AllOf(CF1,CF2,CF3);
观察者的实现类为BiRelay,等所有CF执行完成再触发,实际上就是构造一个平衡二叉树,从底往上层层通知直到根节点,触发回调监听。
注意事项:异步回调要传线程池!!!,如果不传,就会用ForkJoinPool的公共线程池,它的核心线程数=处理器数量-1,可能会造成性能瓶颈。

exceptionally():回调方法,将前一个任务的异常作为入参
whenComplete():某个任务执行完的回调方法,可以拿到上一个方法执行完的结果,无返回值;他返回的依然是上一个任务执行完的结果。
handle():某个任务执行完的回调方法,有返回值,是回调方法执行完的结果。
多任务组合处理:
.thenCombineAsync(task1,(s,w)->{ return... },executor); 把两个任务(一个是task1,一个是当前的任务)的执行结果都作为方法入参传递到指定方法中,并且有返回值。
.applyToEither():将已经执行完成的任务作为方法入参,传递到指定方法中,且有返回值。
.allOf():等所有任务都执行完才返回结果,如果任意一个任务异常,在执行get方法的时候会抛异常
public class allOfFutureTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
CompletableFuture a = CompletableFuture.runAsync(()->{
System.out.println("我执行完了");
});
CompletableFuture b = CompletableFuture.runAsync(() -> {
System.out.println("我也执行完了");
});
CompletableFuture allOfFuture = CompletableFuture.allOf(a, b).whenComplete((m,k)->{
System.out.println("finish");
});
}
}

在我的牛券项目里,当用户使用券进行结算的时候,会查询可用券列表,这个时候我把商品券集合和非商品券集合转化为stream,然后分别创建一个CompletableFutureTask.AllOf任务,等待每一张优惠券对应的任务完成(任务的内容就是校验优惠券当前是否可使用,例如是否满足金额门槛,是否满足商品范围等等)。
这里异步执行需要传入线程池,否则会使用ForkJoinPool的线程,核心线程是CPU核数-1,性能会比较差。
最后调用CompletableFutureTask的AllOf方法,等待两个任务(商品券集合任务,非商品券集合任务)执行结束然后。调用thenRun()方法去对可用优惠券列表的优惠券根据优惠金额进行排序,最后join()阻塞式获取执行结果。

八、CopyOnWriteArrayList
写时复制容器,读的时候不加锁,增删改的时候会加锁并且做同步复制,具体来说就是复制一份原数组,在副本上进行修改,修改结束之后把引用指向新数组。它不能保证强一致性,因为读的时候可能读到的是旧数据,它只能保证最终一致性。
JDK8的时候用的是Reentrantlock加锁,JDK17用的是synchronized加锁
缺点:会产生副本,占用内存,频繁写的情况下会多次复制性能会很差;读的时候可能存在一致性的问题
九、Vector
线程安全的List,使用的是synchronized锁
缺点:
● 它所有方法都是同步的,性能不好。
● 因为vector是线程安全的,所以效率低,这容易理解,类似StringBuffer,同时只能在尾部进行插入和删除操作,更加造成效率低;
● Vector空间满了之后,扩容是一倍,而ArrayList仅仅是一半;
● Vector分配内存的时候需要连续的存储空间,如果数据太多,容易分配内存失败;
十、线程之间如何传递数据
1、TransmittableThreadLocal(TTL)可以进行父子线程间数据传递,父线程创建TTL对象,子线程可以访问TTL内容。
2、线程池内线程共享TTL,使用TtlRunnable类包装Runnable。向线程池里submit TtlRunnable任务。
3、使用TtlExecutor包装线程池,提交任务到Ttl线程池中。
十一、线程池调优
调优的参数:核心线程、最大线程、阻塞队列、拒绝策略
调优的过程:根据CPU密集型和IO密集型、混合型设置线程池参数的初始值,压测根据系统的响应来调整最大线程数(最大线程可能涉及到线程上下文切换带来的开销),选择合适的阻塞队列,对于关键的任务选择CallersRunPolicy,非关键任务可以选择DiscardOldestPolicy。
使用Arthas、Prometheus监测线程池的状态,比如看线程的存活时间、平均处理任务的数量、阻塞队列内的任务数量等。
十二、Collections.synchronizedList()
在我的牛券项目中的用户查询优惠券功能中,我使用CF实现多线程异步编排,这里会并发地将优惠券写入可用和不可用的集合中,此时需要将ArrayList包装成线程安全的SynchronizedList集合。
使用Collections.synchronizedList(new ArrayList<>())
SynchronizedList底层的每一个方法都被synchronized修饰,但是具体的操作依然是交给ArrayList代理,只是操作前需要获得锁。
十三、Synchronized与ReentrantLock的区别
(1)syn无需手动加锁与释放锁,Reen需要
(2)syn是JVM内置语义,可以进行锁粗化和锁消除,Reen是一个AQS的实子类。
(3)Reen可以实现公平锁,还具有响应中断、超时等待等特性。公平锁的吞吐量不高,一般不用。
十四、AQS
AQS主要包含三个内容:
state(volatile变量):获取锁的线程通过CAS修改state的值来获取锁
同步队列:负责锁的获取与释放。线程获取锁失败就会封装成Node节点添加至队列尾部,当锁被释放的时候,头节点被唤醒获取锁。
条件队列:基于Condition接口实现,每一个Condition对象都有一个条件队列。当线程获取到锁之后,不满足条件的线程被挂起进入条件队列,直到其他条件显式唤醒。线程调用await()方法后被加入条件队列等待,直到被另一个线程signal()、signalAll()方法唤醒
同步队列与条件队列的区别:
(1)同步队列用于管理锁获取与释放,条件队列用于让线程等待条件满足
(2)同步队列是自动管理,没有获取到锁自动加入同步队列。条件队列可以通过Condition接口暴露给开发者,并且需要显式调用await和signal方法。
(3)对于AQS,同步队列只有一个,条件队列属于Condition对象,可以有多个。
解决并发问题只使用CAS就够了吗?
CAS只能保证原子性,而不能保证并发下的可见性与有序性。使用volatile可以保证可见性,并且通过禁止指令重排来保证有序性。
所以CAS和volatile是互补的,CAS提供原子性操作提高性能,volatile保证可见性与有序性。
CAS如何保证原子性?
CAS底层基于操作系统(硬件层面)cmpxchg指令实现的,CAS对应值的获取、比较、交换三个动作封装在一条cmpxchg指令当中不可中断。
(1)cmpxchg指令是原子性指令,CPU执行的时候会锁定总线,禁止其他CPU访问共享变量,然后比较与交换之后才释放总线。
(2)cmpxchg指令执行期间会禁止CPU中断,确保原子性。
(3)cmpxchg指令是基于硬件实现的,可以保证原子性。
CAS的可见性保障?
cmpxchg基于MESI缓存一致性协议实现,即为CPU所有核心的缓存都一致,一个核心执行cmpxchg,所有核心都会更新数据,确保对变量访问一致。
CAS的ABA问题如何解决?
附加版本号处理
十五、MESI与JMM
JMM:Java内存模型,定义了java的内存交互的规范来确保原子性、可见性和有序性。对于原子性,可以通过synchronized、volatile确保原子性;对于可见性,可以通过volatile、happens-before原则确保;对于有序性,可以通过volatile、sybchronized确保。
MESI:硬件层面的缓存一致性协议,用于确保多核处理器中不同处理器的缓存一致性。主要通过四个状态MESI(Modified、Exclusive、Shared、Invalid)来保证有序性。
具体来说,当一个处理器要修改缓存行数据时,先检查状态
如果时E、S状态,则直接读缓存中的数据,因为此时数据一致
如果是I状态,则读取共享内存中的数据到缓存,再处理
如果是M,则先刷新到主内存,再读主内存数据。
十六、volatile
volatile是java提供的一种轻量级同步机制,因为他不会产生synchronized那样的线程上下文切换。
可见性:volatile变量,JMM会被线程工作内存中信息刷到主内存中。
有序性:volatile会禁止指令重排保证有序性。
不保证原子性:
十七、虚拟线程
定义:虚拟线程是一种轻量级线程(用户线程),它时线程的另一种实现方式,它不同于之前的平台线程对操作系统线程进行封装,虚拟线程时将多个虚拟线程映射到少量的操作系统线程中,可以轻松做到百万级虚拟线程。
传统的平台线程是对操作系统的包装,在线程调度中存在用户态和内核态的切换,开销很大。此外平台线程的数量受到操作系统线程的影响。这样存在的问题:线程空闲状态下没有被释放(例如IO线程),而此时CPU利用率低,导致并发请求过来时无法创建更多线程进而影响并发性能。
过去情况是用异步编程提高并发量,但是异步编程存在大量回调函数,维护成本也很高。
轻量级线程由JVM管理的。传统线程类似于墙上的插座,插座被占满了,但是有些插座压根不通电,虚拟线程相当于在插座上接一个排插出来增加接口数量。
当把虚拟线程名称打印出来时,发现虚拟线程的载体依然是一个平台线程,当运行一百万个虚拟线程,发现平台线程只用了8个。
虚拟线程的载体是平台线程,平台线程有一个任务列表,里面有很多虚拟线程,这些平台线程处于Fork Join Pool中,当虚拟线程阻塞的时候会从平台线程卸载,然后当它恢复之后可以绑定到其他平台线程,也就是说虚拟线程与平台线程不是一一对应的,可以被重新调度到其他平台线程。
虚拟线程使用Thread.ofVirtual()创建
此外,它的创建与销毁中的内存开销低,上下文切换低。
使用:
Thread.startVirtualThread(()->{
System.sout.println();
});
虚拟线程的优势:
1、轻量级线程,不像平台线程那样会有很高的堆栈内存占用
2、线程的上下文切换、创建、销毁的开销小。
3、传统线程模型当IO阻塞时会导致整个线程被阻塞,并且阻塞唤醒有开销,而虚拟线程阻塞的时候可以直接挂起,开销小。
劣势:
1、高并发与高吞吐量,但是做不到低延迟
2
适用场景:
1、阻塞型任务,阻塞期间可以将平台线程资源让渡给其他任务,等结束阻塞之后再绑定到其他平台线程。
2、不适合计算密集型或者CPU密集型,因为它依然是基于平台线程,只是增加了并行的规模。
3、例如Tomcat、Netty都已经支持虚拟线程。

相关文章:

JUC 学习笔记

并发与并行 并发:多个线程轮流使用CPU执行任务,将CPU的时间分割成合适大小的时间片,每一个线程拿到时间片之后就会利用CPU资源执行任务,当时间到了之后就会把CPU资源释放,并且保存线程的上下文,比如程序计数器,栈帧等资源。由于CPU执行速度很快,所以用户肉看看不出程序…...

pytorch读书报告

PyTorch作为开源深度学习框架,以动态计算图、简洁API和强大生态深受开发者青睐。 通过学习相关资料,我了解到其核心优势在于“即插即用”的动态图机制——代码编写与调试时可实时修改计算流程,无需提前定义静态图,大幅降低了深度学习入门门槛。 PyTorch与Python生态深度融合…...

券多多系统-开发记录

项目整体分为后管模块、分发模块、引擎模块、结算模块。 后管模块:面向商家,负责优惠券的创建、以及目标用户的优惠券分发任务 分发模块:面向商家,负责执行百万级别优惠券的分发任务 引擎模块:面向用户,为用户提供优惠券查询、兑换、秒杀以及秒杀活动预约等功能,并在在用…...

Java笔记

String、StringBuilder、StringBuffer String不可变,StringBuilder和StringBuffer可变,只有StringBuffer是线程安全的。 String不可变的四个原因: (1)类被final修饰不可被继承 (2)底层char[]数组被final修饰 (3)提供的方法返回值都是new一个新String对象 (4)字符串常…...

分布式 笔记

1、分布式锁的实现方式: 1、数据库 基于数据库表: 创建一张锁表:id、方法名称(唯一索引)、备注信息、参数信息(对应重载方法)、更新时间 锁住一个方法等价于往锁表里插入一行数据,通过方法名称字段的唯一性把其他想获得方法的线程给校验住 缺点:数据库挂了就很麻烦,需…...

Windows Server 2019 中文版、英文版下载 (2025 年 9 月更新)

Windows Server 2019 中文版、英文版下载 (2025 年 9 月更新)Windows Server 2019 中文版、英文版下载 (2025 年 9 月更新) Windows Server 2019 x64 Version 1809 (updated Sep 2025) 请访问原文链接:https://sysin.org/blog/windows-server-2019/ 查看最新版。原创作品,转载…...

Windows Server 2016 中文版、英文版下载 (2025 年 9 月更新)

Windows Server 2016 中文版、英文版下载 (2025 年 9 月更新)Windows Server 2016 中文版、英文版下载 (2025 年 9 月更新) Windows Server 2016 x64 Version 1607 (updated Sep 2025) 请访问原文链接:https://sysin.org/blog/windows-server-2016/ 查看最新版。原创作品,转载…...

Windows Server 2025 中文版、英文版下载 (2025 年 9 月更新)

Windows Server 2025 中文版、英文版下载 (2025 年 9 月更新)Windows Server 2025 中文版、英文版下载 (2025 年 9 月更新) Windows Server 2025 LTSC x64 (updated Sep 2025) 请访问原文链接:https://sysin.org/blog/windows-server-2025/ 查看最新版。原创作品,转载请保留出…...

美联储降息 25 个基点,这事儿跟我们有多大关系?

正文今天聊个有时效性的东西,美联储降息。25 个点。17 号晚上公布的。当然那个时候我还在睡觉,我虽然关心美联储,但是不至于连夜追。身体健康还是比 FED 啊、鲍威尔啥的重要多了。有些人可能会觉得这个事儿挺遥远的。但其实并不是。想想我们银行利率降到这么低就懂了。现在银…...

Windows Server 2022 中文版、英文版下载 (2025 年 9 月更新)

Windows Server 2022 中文版、英文版下载 (2025 年 9 月更新)Windows Server 2022 中文版、英文版下载 (2025 年 9 月更新) Windows Server 2022 x64, Version 21H2 (updated Sep 2025) 请访问原文链接:https://sysin.org/blog/windows-server-2022/ 查看最新版。原创作品,转…...

袋鼠云跻身榜单2025中国Data+AI创新企业榜Top15,入选“2025中国隐形独角兽500强”榜单等多项荣誉

一、袋鼠云跻身榜单2025中国Data+AI创新企业榜Top15、2025中国垂直AI Agent创新企业榜TOP30为响应AI产业“技术深化+规模落地”的关键进阶期,国内IT市场研究与咨询机构「第一新声」正式启动了第三届【AI创新先锋—2025中国AI产业创新先锋榜单】评选活动,旨在发掘并表彰受资本…...

k8s系列--前世今生

基础设施即服务 阿里云,国外IWS 平台即服务 新浪云 --docker成为了平台即服务的下一代标准 软件设施即服务 office 为什么需要K8s,传统的架构不需要k8s,但是引入docker容器化技术以后,容器内部的访问变得复杂了,容器的集群化,有没有好的产品,有需求就有产品 Apache MESOS…...

excel文本改为数据格式

excel文本改为数据格式原理和方法转自:https://zhuanlan.zhihu.com/p/179945111 1)选中列(不能有合并的列)---数据----分列-----完成。 2)选中多列,必须是有绿三角形的开始----------电机加圈的i------------------转换为数字...

面向对象初步接触-学生信息管理系统

1. 功能讲解 1.1 数据处理该程序主要处理学生数据,包含学生的name、age、gender、id、major、gpa这些基本信息。 学生数据存储在StudentManagementSystem类的students成员变量中,每个学生的具体信息封装在Student类的对象中,这些对象被添加到List集合中统一管理。 不同之处:…...

Numpy高维数组的索引()

# 创建一个形状为 (3, 4, 2) 的随机数组 # arr 代表 3 个实例,每个实力有 4 个任务点,每个任务点有 (x, y) 坐标 arr = np.random.randint(0, 10, size=(3, 4, 2))arr:[[[8 4][2 9][0 5][4 7]][[5 5][1 6][8 5][1 8]][[6 2][5 5][4 5][9 6]]]索引或切片 描述 这里的ijk都要加…...

详细介绍:jQuery 操作指南:从 DOM 操作到 AJAX

详细介绍:jQuery 操作指南:从 DOM 操作到 AJAXpre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", monospace …...

一个 Blazor/WinForm 开发者的 WPF 学习记:通往 Avalonia 的那条路

一个 Blazor/WinForm 开发者的 WPF 学习记:通往 Avalonia 的那条路 写在前面 做了几年 Blazor 和 WinForm,本来以为桌面端这件事就这么过去了。直到我认真考虑跨平台桌面方案,才发现绕不开 Avalonia。而要真正用好 Avalonia,最好先补 WPF 这一课。这篇文章不是教程,更多是…...

VulkanAPI细节梳理2

// --- 创建渲染通行证(支持多视图和深度缓冲) ---std::vector<VkAttachmentDescription> attachments(2);// 颜色附件attachments[0].format = format;attachments[0].samples = VK_SAMPLE_COUNT_1_BIT;attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;attachme…...

React 状态丢失:组件 key 用错引发的渲染异常 - 指南

React 状态丢失:组件 key 用错引发的渲染异常 - 指南pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", monos…...

快速实现 Excel 表格转 SVG:Java 教程 - E

如果你曾尝试过把 Excel 表格导出为 SVG 图片,就会发现 Excel 本身并不支持这种格式。虽然市面上存在一些在线转换工具,但上传文件存在效率与安全方面的顾虑。对于开发者来说,使用 Java 来实现 Excel 到 SVG 的转换,是一种更灵活、可控的方式。本文将演示如何通过简单的 Ja…...

绕过文件上传限制实现客户端路径遍历漏洞利用的技术解析

本文详细解析了如何通过精心构造JSON文件绕过PDF和图像上传验证机制,利用mmmagic、pdflib和file命令的检测特性实现客户端路径遍历攻击的技术方法与实战案例。绕过文件上传限制实现客户端路径遍历漏洞利用 在我之前的博客文章中,我演示了如何利用JSON文件作为客户端路径遍历(…...

事件总线之初步学习

第一步:创建一个eventBus.js 文件名可根据个人爱好取名即可; 内容:import Vue from vue const eventBus = new Vue(); export default eventBus;第二步:使用import EventBus from @/common/eventBus;//监听全局事件clear-login-interval EventBus.$on(clear-login-interva…...

Markdown Day04

常用DOS命令 ##查看当前目录下所有文件dir ##切换目录cd change directory ##cd..回到上一个 ##清理屏幕cls ##退出exit ##查看电脑IP,ipconfig ##打开应用 calc notepad mspaint ##ping 命令 ping www.baidu.com ##文件操作 ad 创建目录 cd> 新增文件 del 删除文件 rd 移…...

C++中类的内存存储

目录类类对象的非虚成员函数类的成员变量空类对象具有虚函数的类对象 类 类本身不会存储在内存中,类实例化的对象才会保存在内存中。但是使用 sizeof 计算类大小时能得到结果,这是因为 sizeof 会在编译时就得到类型信息,这只与类的布局有关。 类对象的非虚成员函数 考虑如下…...

PyTorch 优化器(Optimizer)

优化器(Optimizer)是深度学习训练过程中用于更新模型参数的核心组件。在训练神经网络时,我们需要通过反向传播计算损失函数相对于模型参数的梯度,然后使用优化器根据这些梯度来更新参数,以最小化损失函数。PyTorch 优化器(Optimizer)技术指南 目录优化器概念 常用优化器…...

实用指南:域名市场中,如何确认域名的价值

实用指南:域名市场中,如何确认域名的价值pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", monospace !impo…...

初步了解Neo4j

1. 是什么? Neo4j 是一个原生图数据库。它与我们熟悉的关系型数据库(如 MySQL, PostgreSQL)和 NoSQL 数据库(如 MongoDB)有根本性的不同,因为它专门为存储和查询数据之间的关系而设计。 它的核心哲学是:“关系即一等公民”。这意味着关系(或连接)和数据本身同等重要,…...

多模态和语音 AI 年度收官大会,把握 2026 技术风向标!

如果今年只参加一场多模态和语音 AI 大会,来 Convo AI & RTE2025 就够了。你是否好奇:1⃣从端到端语音模型和全双工技术,未来音频还将有哪些突破方向?2⃣如何挖掘端侧 AI 潜能,定义下一代智能硬件终端?3⃣从主动智能体到可交互世界模型,实时多模态 AI 将驱动怎样的未…...

做题

P4159 [SCOI2009] 迷路 矩阵快速幂优化递推。 首先最暴力的想法,设 $ f_{i,j} $ 在 $ j $ 时刻,到达点 $ i $ 的种类数。 枚举时间和 $ i $,然后 $ f_{i,j} = \sum\limits_{k=1}^n {f_{k,j-w[i][k]}} $。 时间很大,所以肯定行不通。 这样显然是不能用矩阵来优化的。 考虑拆…...

解码C语言函数

一、函数基本概念 1.1 函数定义 概念:把一个功能的实现流程封装起来,使用户留下接口进行调用 作用:参数创建该功能进行封装操作,返回值即通过功能显示的产出 1.2 函数组成要素返回值类型:根据函数功能而定,需要在函数头中指定子程序类的返回值类型 参数名:在函数体中提取…...

SchemaStore

Hello World本文来自博客园,作者:南宫影,转载请注明原文链接:https://www.cnblogs.com/nangongying/p/19100468...

XSS攻击防御

目录背景和价值为什么需要输出编码?不同场景下的编码方式(白话版)1. 最常见场景:内容显示在HTML标签里(比如<div>、<span>中)2. 特殊场景:内容显示在HTML标签的属性里(比如value、href中)3. 特殊场景:内容要在JavaScript代码里使用4. 特殊场景:内容作为…...

imes开发部署

一.git地址下载源码 二.后端设置 1.增加配置文件:ktg-admin/src/main/resources/application-test.yml2.临时修改代码:ktg-mes/src/main/java/com/ktg/mes/task/MesTask.java,如图注释2行。 3.快捷键Ctrl+Alt+Shift+S,打开项目结构(Project Structure)对话框4.编辑配置5…...

思维题做题记录-1

CF2600左右有趣的思维题做题记录-1 CF1458C. Latin Square 考虑将原矩阵写成 \(n\times n\) 个限制形如 \((i,j,a_{i,j})\),那么所有操作就是对这些限制进行的修改:对于 UD 操作相当于将限制改为 \((i\mp 1,j,a_{i,j})\)。 对于 LR 操作相当于将限制改为 \((i,j\mp 1,a_{i,j}…...

如何在极短时间内通透一个大型开源项目

如何在极短时间内通透一个大型开源项目前言 在现代软件开发中,快速理解和掌握大型开源项目是一项至关重要的技能。无论是参与开源贡献、技术选型,还是学习先进架构模式,都需要我们具备高效解读项目的能力。本文将以 OpenDeepWiki 项目为例,深入剖析如何运用AI技术快速通透一…...

求 Ray Ping - Gon

rt(跟风...

LCT学习笔记

LCT学习笔记从例题开始: P3690 【模板】动态树(LCT) 对于一棵静态的树,常见方法是树剖然后走链,但是在动态的情况下常见的重链或长链就会很慢,因为修改连边情况后就不满足性质了 引入一个新的方法:实链剖分,对于一个节点,任选一个儿子,连边为实边,其余为虚边,注意这…...

Visual Studio 2026 Insiders 重磅发布:AI 深度集成、性能飞跃、全新设计

近日,微软正式发布 Visual Studio 2026 Insiders!这是迄今为止 Visual StudioE 极具跨越式的一次升级。新版本不仅将 AI 深度融入开发工作流,还带来了企业级性能的显著提升,以及更轻盈、现代的界面设计,全面提升开发体验。近日,微软正式发布 Visual Studio 2026 Insiders…...

《刚刚问世》系列初窥篇-Java+Playwright自动化测试-29- 操作单选和多选按钮 - 下篇(详细教程) - 北京

1.简介 我们可能会遇到一直测试单选和复选按钮的测试场景,如果就十几道选择题,那就手工点击,马上完事,但是如果是让你测试题库呢?那不得那鼠标点击冒烟了,手指点到抽筋了。尤其是做教育类的软件测试,这些就是家常便饭了。因此今天这一篇宏哥主要是讲解一下,如何使用Pla…...

自定义注解实现服务分处理-策略模式

路由:请求标识→匹配 Service→调用 process 方法 通过自定义注解 @BusinessServiceMapping 标记具体业务 Service,注解值(如 DC 代表客户、ORD 代表订单)与请求参数中的业务标识关联;再通过 Spring 容器扫描 + 策略模式,实现 “请求标识→匹配 Service→调用 process 方…...

iOS26正式版全新风格!一文汇总实用新功能!

苹果在9月17日凌晨正式推送了iOS26系统更新,这次版本更新带来了多达61项新功能与优化。经过9个Beta版和近100天的测试,iOS26正式版终于与用户见面,版本号为23A340,更新包大小约8GB。 iOS26不仅在设计语言上焕然一新,更在AI能力、交互体验和隐私保护等多个维度进行了全面升…...

远程控制应用的中的全球节点功能如何开启?插件类型、并发数量怎么选?

不知道大家使用远程控制应用进行跨系统跨设备操作主要都是针对哪些场景呐?其实对于很多需要跨境远程办公的人群或进行售后设备升级管理的朋友来说无疑是必不可少的,甚至于海外学子们来说同样也至关重要,毕竟总有需要协助国内亲友处理问题的时刻,代操作远高于语音指导的效率…...

借助Aspose.HTML控件,使用 Python 将 HTML 转换为 DOCX

Aspose.HTML for Python via .NET提供了用于自动执行文件格式转换任务的类和方法。此外,它能够精确地转换 HTML 结构和样式,是 Python 开发人员的理想选择。本教程将向开发者展示如何在 Python 中以编程方式将HTML转换为DOCX。我们将使用一个非常快速的 Python SDK 将网页转换…...

openEuler 24.03 (LTS-SP2)安装mysql 8.0.41

环境:OS:openEuler 24.03 (LTS-SP2)(安装时候没有图形界面的选择项可选)mysql:8.0.41 glib.2.17 操作系统下载https://www.openeuler.org/en/download/#openEuler%2024.03%20LTS%20SP2查看系统glibc版本[root@localhost soft]# ldd --versionldd (GNU libc) 2.38Copyright (C) …...

7.数据库归档异常检查与处理

备库: select instance_name,status from v$instance; select open_mode from v$database; @dgstat 如果都是00:00:00则说明本地从生产到DR同步没有问题 @dgpro 与上面的RFS的sequence#进行对比,可以算出生产与DR相差的sequence Block# 1分钟前是852100,1分钟后是856100,…...

Gitlab 关键字

核心原则:一切路径始于项目根目录:https://blog.csdn.net/qq_14829643/article/details/150773286include: local:中的所有路径都是相对于当前项目的根目录进行解析的。它既不是传统意义上的“绝对路径”(如 /etc/gitlab-runner/config.toml,这会指向 Runner 服务器的文件…...

8.listener日志占用过大处理方法

ps -ef |grep tns asmenv 查询listener.log的位置路径 lsnrctl status [listener name] 例如: listener log file : /oracle/TEST/diag/tnslsnr/xianigux/listener/alert/log.xml cd /oracle/TEST/diag/tnslsnr/xianigux/listener/alert 删除除了log.xml以外的所有xml文件 rm …...

马建仓AI助手完成全链路升级:三十余项新能力重塑研发工作流

马建仓AI助手完成全链路升级:三十余项新能力重塑研发工作流 在数字化转型浪潮席卷各行各业的当下,研发效率正成为企业竞争力的关键指标。马建仓AI助手近日宣布完成面向真实研发流程的全面升级,新增三十余项智能能力,覆盖需求、开发、测试、项目管理等关键环节,标志着企业级…...

线性回归与 Softmax 回归:深度学习基础模型解析 - 实践

线性回归与 Softmax 回归:深度学习基础模型解析 - 实践pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", mon…...

浏览器下载,一定要开启这个隐藏功能!

大家好,我是 Immerse,一名独立开发者、内容创作者。关注公众号:沉浸式趣谈,获取最新文章(更多内容只在公众号更新) 个人网站:https://yaolifeng.com 也同步更新。 转载请在文章开头注明出处和版权信息。我会在这里分享关于编程、独立开发、AI干货、开源、个人思考等内容…...