JUC并发编程2(高并发,AQS)
JUC
AQS核心
当有线程想获取锁时,其中一个线程使用CAS的将state变为1,将加锁线程设为自己。当其他线程来竞争锁时会,判断state是不是0,不是自己就把自己放入阻塞队列种(这个阻塞队列是用双向链表实现),当这个线程使用完,会把state变为0,该state使用volatile修饰。在AQS内部,每个Node节点都是等待锁的线程,队列中每个排队的个体就是一个Node节点,它的等待状态waitState成员变量,也是volatile修饰,Node节点里也记录该线程是否不再等待状态,还记录锁的模式独占锁还是共享锁。
三大核心:
- state 状态,代表加锁状态,初始值是0 (state是被volatile修饰)
- 获取到锁的线程
- 还有一个阻塞队列(双向队列)
AQS锁有关:
- ReentrantLock(可重入锁)
- ReentrantReadWriteLock.ReadLock(可重入读写锁中的读锁)
- ReentrantReadWriteLock.WriteLock(可重入读写锁中的写锁)
- CountDownLatch
- CyclickBarrier
- Semaphore
Lock使用方式:
Lock lock = new ReentrantLock();public void doTicket(){ lock.lock(); //加锁try {System.out.println(Thread.currentThread().getName());} catch (Exception e) {e.printStackTrace();} finally {lock.unlock(); // 解锁}}
Synchronized 和 Lock区别
-
Synchronized 内置的Java关键字,Lock是一个Java类
-
Synchronized 无法判断获取锁的状态,Lock可以判断
-
Synchronized 会自动释放锁,lock必须要手动加锁和手动释放锁!可能会遇到死锁
-
Synchronized 是可重入锁,不可以中断的,非公平的;Lock,可重入的,可以判断锁,可以自己设置
读写锁
ReadWriteLock
- 读写锁:更加细粒度的锁
- 读-读:可以共存
- 读-写:不能共存
- 写-写:不能共存
它允许读读共存,读写和写写是互斥的,适合读多写少的场景,但会有写锁饥饿问题。
锁降级:写锁可以降级到读锁,但读锁不能升级到写锁;
public class ReadWriteLockDemo {public static void main(String[] args) {MyCache mycache = new MyCache();//开启5个线程 写入数据for (int i = 1; i <=5 ; i++) {int finalI = i;new Thread(()->{ mycache.put(String.valueOf(finalI),String.valueOf(finalI));}).start();}//开启10个线程去读取数据for (int i = 1; i <=10 ; i++) {int finalI = i;new Thread(()->{String o = mycache.get(String.valueOf(finalI));}).start();}}
}class MyCache{private volatile Map<String,String> map = new HashMap<>();//普通锁//private Lock lock = new ReentrantLock();//使用读写锁private ReadWriteLock lock = new ReentrantReadWriteLock();public void put(String key,String value){//写锁lock.writeLock().lock();try {//写入System.out.println(Thread.currentThread().getName()+" 线程 开始写入");map.put(key, value);System.out.println(Thread.currentThread().getName()+" 线程 写入完成");} finally {lock.writeLock().unlock();}}public String get(String key){//读锁lock.readLock().lock();String o;try {System.out.println(Thread.currentThread().getName()+" 线程 开始读取");o = map.get(key);System.out.println(Thread.currentThread().getName()+" 线程 读取完成");} finally {lock.readLock().unlock();}return o;}
}
对于读取,我们运行多个线程同时读取,也能在一定程度上提高效率。
StampedLock
JDK8新增的读写锁(邮戳锁),它采用乐观锁,其他线程尝试获取锁不会被阻塞,对读锁优化。获取锁的方法,返回一个邮戳Stamp,邮戳Stamp为0表示获取失败,其他是成功;释放锁的方法,需要一个邮戳Stamp与成功获取锁的邮戳Stamp一致,但它不支持可重入锁;
StampedLock stampedLock = new StampedLock();
// 乐观锁
public void tryOptimisticRead() {long stamp = stampedLock.tryOptimisticRead(); // 乐观读int result = number; // 获取一下最新的值//判断是否发生改变 stampedLock.validate()System.out.println("alidate方法值(true无修改,false有修改)" + "\t" + stampedLock.validate(stamp));if (!stampedLock.validate(stamp)) {System.out.println("有人修改过------有写操作");stamp = stampedLock.readLock();try {System.out.println("从乐观读 升级为 悲观读");result = number;System.out.println("重新悲观读后result:" + result);} finally {stampedLock.unlockRead(stamp);}}
}
常用的辅助类
CountDownLatch
这个类使一个线程等待其他线程各自执行完毕后再执行。
主要方法:
- countDown 减一操作;
- await 等待计数器归零。
public static void main(String[] args) throws InterruptedException {//总数6个CountDownLatch countDownLatch = new CountDownLatch(6);for (int i = 1; i <= 6; i++) {new Thread(()->{System.out.println(Thread.currentThread().getName() +" 执行do");//每个线程都数量-1countDownLatch.countDown();},String.valueOf(i)).start();}//等待计数器归零countDownLatch.await();System.out.println("必须其他线程都执行完,在执行这里");//最后执行的...
}
CyclickBarrier
用于对多个线程任务进行同步执行。
主要方法:
- await 在所有线程任务都到达之前,线程任务都是阻塞状态
public static void main(String[] args) {CyclicBarrier cyclicBarrier = new CyclicBarrier(7,()->{System.out.println("召唤神龙的线程~");});for (int i=1;i<=7;i++){int atI = i;new Thread(()->{try {System.out.println(Thread.currentThread().getName()+" 收集了第" + atI +"颗龙珠");cyclicBarrier.await(); //加法计数 等待} catch (InterruptedException e) {e.printStackTrace();} catch (BrokenBarrierException e) {e.printStackTrace();}},"线程"+i).start();}
}
应用:
- CyclickBarrier可以根据基于子线程进行处理其他线程的结果,处理比较复杂的业务。并且可以通过reset方法重新执行方法。
- CountDownLoatch则必须在主线程才能处理,一般用于任务执行初始化数据
Semaphore
信号量,在信号量定义两种操作:
- acquire(获取)当一个线程调用acquire操作,它通过成功获取信号量(信号量-1),有阻塞,直到有线程释放信号量,或者超时。
- release(释放)实际上将信号量的值+1,然后唤醒等待的线程。
public static void main(String[] args) {//停车位为3个Semaphore semaphore = new Semaphore(3);for (int i=1 ; i<=10; i++){int atI = i;new Thread(()->{try {semaphore.acquire(); //得到System.out.println(Thread.currentThread().getName() + " 抢到停车位" + atI);TimeUnit.SECONDS.sleep(2);System.out.println(Thread.currentThread().getName() + " 离开停车场");} catch (Exception e) {e.printStackTrace();} finally {semaphore.release(); //释放}},"线程"+i).start();}
}
作用: 多个共享资源互斥的使用! 并发限流,控制最大的线程数!
异步回调
CompletableFuture
就是另启一个线程来完成调用中的部分计算,使调用继续运行或返回,而不需要等待计算结果。一个completetableFuture就代表了一个任务。相当于前端的ajax异步请求。
没有返回值的异步回调:
@Test
void test1() throws ExecutionException, InterruptedException {System.out.println(System.currentTimeMillis());System.out.println("---------------------");CompletableFuture<Void> future = CompletableFuture.runAsync(()->{//发起一个异步任务try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+".....");});System.out.println(System.currentTimeMillis());System.out.println("------------------------------");//输出执行结果System.out.println(future.get()); //获取执行结果
}
有返回值的异步回调supplyAsync:
void test2() throws ExecutionException, InterruptedException {System.out.println(System.currentTimeMillis());System.out.println("---------------------");//有返回值的异步回调CompletableFuture<Integer> completableFuture=CompletableFuture.supplyAsync(()->{System.out.println(Thread.currentThread().getName());try {TimeUnit.SECONDS.sleep(2);//自定义手动的异常int i=1/0;} catch (InterruptedException e) {e.printStackTrace();}return 1024;});System.out.println(completableFuture.whenComplete((t, u) -> {//success 回调System.out.println("t=>" + t); //正常的返回结果System.out.println("u=>" + u); //抛出异常的 错误信息}).exceptionally((e) -> {//error回调 如果异常,返回404System.out.println(e.getMessage());return 404;}).get());
}
ThreadLocal
它是线程本地变量,解决多线程并发时访问共享变量的问题。它并不解决线程之间共享数据的问题,它适用于变量在线程之间隔离且在方法间共享的场景,每个线程持有一个只属于自己的专属的Map并维护ThreadLocal对象与具体实例的映射,该Map只有被持有它的线程访问,就没有线程安全问题以及锁的问题。
# 使用
static ThreadLocal<Object> threadLocal = new ThreadLocal<>();# 初始化
private static final ThreadLocal<Object> threadLocal = ThreadLocal.withInitial(Object::new);
private static final ThreadLocal<Object> threadLocal = ThreadLocal.withInitial(()->{初始化的值});# 设置线程本地变量的内容
threadLocal.set(user);# 获取线程本地变量的内容
threadLocal.get();# 移除线程本地变量
threadLocal.remove();
线程中断机制
一个线程中断,应该由线程自己停止,不能由其他线程进行停止。
# 中断此线程
interrupt()
# 判断当前线程是否被中断
isInterrupted()
# 案例
Thread t1 = new Thread(() -> {while (true) {// Thread.currentThread().isInterrupted() 判断线程是否被中断,true:中断状态if (Thread.currentThread().isInterrupted()) {System.out.println(Thread.currentThread().getName() + "\t 【实现方式3】,程序停止");break;}System.out.println("t1 -----正在运行!!!");
} }, "t1");
t1.start();
System.out.println("-----t1的默认中断标志位:" + t1.isInterrupted());
LockSupport
可以阻塞当前线程以及唤醒指定被阻塞的线程,有park()和unpark()进行通知与唤醒。
Thread t1 = new Thread(() -> {TimeUnit.SECONDS.sleep(3);System.out.println(Thread.currentThread().getName() + "\t ----【t1】开始执行" + System.currentTimeMillis());LockSupport.park();System.out.println(Thread.currentThread().getName() + "\t ----【t1】 被唤醒" + System.currentTimeMillis());
}, "t1");
t1.start();new Thread(() -> {LockSupport.unpark(t1);System.out.println(Thread.currentThread().getName() + "\t 【t2】唤醒t1线程----发出通知");}, "t2").start();
volatile关键字
Volatile 是 Java 虚拟机提供 轻量级的同步机制。
volatile三大特性
- 保证可见性 (要么都完成,要么都不完成)
- 不保证原子性
- 禁止指令重排
JMM:Java内存模型,是java虚拟机规范中所定义的一种内存模型,Java内存模型是标准化的,屏蔽掉了底层不同计算机的区别(注意这个跟JVM完全不是一个东西,现在还有小伙伴搞错的)。
关于JMM的一些同步的约定
- 线程解锁前,必须把共享变量立刻刷回主存。
- 线程加锁前,必须读取主存中的 新值到工作内存中!
- 加锁和解锁是同一把锁
首先我们要了解JMM (Java内存模型),线程分为 工作内存 、主内存。 图中,有两个线程,每个线程都有属于自己的工作内存;我们首先在主存中有Flag变量,有一个线程A,从主存中读取值,把值加载工作内容中,这样工作内存中也有一个Flag变量。线程A修改值先修改工作内容,并把值再写入到主存中。这个流程叫内存交互。

内存交互有8种:
- lock (锁定):作用于主内存的变量,把一个变量标识为线程独占状态
- unlock (解锁):作用于主内存的变量,它把一个处于锁定状态的变量释放,释放后的变量才可以被其他线程锁定
- read (读取):作用于主内存变量,它把一个变量的值从主内存传输到线程的工作内存中,以便 随后的load动作使用
- load (载入):作用于工作内存的变量,它把read操作从主存中变量放入工作内存中
- use (使用):作用于工作内存中的变量,它把工作内存中的变量传输给执行引擎,每当虚拟机 遇到一个需要使用到变量的值,就会使用到这个指令
- assign (赋值):作用于工作内存中的变量,它把一个从执行引擎中的值放入工作内存的变量副本中
- store (存储):作用于主内存中的变量,它把一个从工作内存中一个变量的值传送到主内存中, 以便后续的write使用
- write (写入):作用于主内存中的变量,它把store操作从工作内存中得到的变量的值放入主内存的变量中
问题: 程序不知道主内存的值已经被修改过了,但是volatile关键字,可以解决这个问题。
例子:private volatile boolean flag = false;

volatile 是保证原子性的,
原子性 : 不可分割
线程A在执行任务的时候,不能被打扰的,也不能被分割。要么同时成功,要么同时失败。
解决原子性问题,加锁/使用原子类
原子变量
原子变量:来源于 java.util.concurrent.atomic 类的小工具包,支持在单个变量上解除锁的线程安全编程,包下提供了常用的原子变量:
- AtomicBoolean 、AtomicInteger 、AtomicLong 、 AtomicReference
- AtomicIntegerArray 、AtomicLongArray
- AtomicMarkableReference
- AtomicReferenceArray
- AtomicStampedReference
类中的变量都是volatile类型:保证内存可见性
使用CAS算法:保证数据的原子性
例子:
public class VDemo02 {// volatile 不保证原子性// private volatile static int num = 0;// 原子类的 Integerprivate volatile static AtomicInteger num = new AtomicInteger();public static void add(){// num++; // 不是一个原子性操作// 原子类的 加减 // AtomicInteger + 1 方法, CASnum.getAndIncrement();}public static void main(String[] args) {//理论上num结果应该为 2 万for (int i = 1; i <= 20; i++) {new Thread(() -> {for (int j = 0; j < 1000; j++) { add(); }}).start();}while (Thread.activeCount() > 2) { Thread.yield();}System.out.println(Thread.currentThread().getName() + " " + num);}}
指令重排
你写的程序,计算机并不是按照你写的那样去执行的。
源代码–>编译器优化的重排–> 指令并行也可能会重排–> 内存系统也会重排—> 执行
Volatile 是可以保持 可见性。不能保证原子性,由于内存屏障(一种屏障指令,使用CPU对屏障指令前后发出内存操作,执行一个排序约束),可以保证避免指令重排的现象产生!
CAS算法
(Compare-And-Swap) 是一种硬件对并发的支持,针对多处理器操作而设计的处理器中的一种特殊指令,用于管理对共享数据的并发访问。
CAS 比较并交换,当且仅当V==A时,B的值才更新给A,否则将不做任何操作。
public static void main(String[] args) {// 原子类AtomicInteger atomicInteger = new AtomicInteger(2020);// public final boolean compareAndSet(int expect, int update) ,参数1 期望的值,参数2,更新的值// 如果我期望的值达到了,那么就更新,否则,就不更新, CAS 是CPU的并发原语!// compareAndSet 也是CASatomicInteger.compareAndSet(2020, 2021);// 输出值System.out.println(atomicInteger.get());
}
Unsafe 类:
底层调用这个类,JAVA无法操作内存,C++可以操作内存,但是JAVA可以调用C++,Unsafe 类,就是java可以通过这个类操作内存。
CAS : 比较当前工作内存中的值和主内存中的值,如果这个值是期望的,那么则执行操作!如果不是就 一直循环!

缺点:
- 循环会耗时
- 一次性只能保证一个共享变量的原子性
- ABA问题
解决ABA 问题
比如一个线程1从内存位置V中取出A ,线程2也执行,将A–>B–>A,这时线程1进行CAS操作发现内存仍是A,然后线程1操作成功。
就是有一个线程速度快,把一个值改变,然后在改变原来的值,其他线程进行CSA,比较并交换,发现值一样,替换新值,并不知道他之前有人已经改过值了。
解决这问题:
- 将类变成原子类
- 操作过程添加版本号
// 原子引入
static AtomicStampedReference<Integer> atomic = new AtomicStampedReference<>(1,1);
public static void main3(String[] args) {new Thread(()->{// 获得版本号int stamp = atomic.getStamp();System.out.println("线程a1=>"+stamp);try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}// 比较并交换 ,增加一个参数,版本号 ,然后再修改版本号的值// 获取最新的版本号 atomic.getStamp()atomic.compareAndSet(1, 2, atomic.getStamp(), atomic.getStamp() + 1);System.out.println("线程a2=>"+ atomic.getStamp());System.out.println(atomic.compareAndSet(2, 1, atomic.getStamp(), atomic.getStamp() + 1));System.out.println("线程a3=>"+atomic.getStamp());},"线程A").start();// 乐观锁的原理相同!new Thread(()->{int stamp = atomic.getStamp(); // 获得版本号System.out.println("线程b1=>"+stamp);try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(atomic.compareAndSet(1, 6, stamp, stamp + 1));System.out.println("线程B2=>"+atomic.getStamp());},"线程B").start();}
原子类
基本类型类:AtomicBoolean、AtomicIntegern、AtomicLong
数组类型类:AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray
引用类型类:AtomicReference、AtomicReferenceFieldUpdater、AtomicMarkableReference、AtomicMarkableReference
LongAdder的效率比AtomicLong高(减少自旋次数)
AtomicLong
- 原理:CAS + 自旋(它是多线程对单个热点值进行原子操作)
- 缺点:当线程大量自旋会导致CPU生高,
LongAdder
- 原理:CAS + Base + Cell数组(用空间换时间,采用分散热点数据)
- 缺点:它的sum求和,对于最后结果不够准确
相关文章:

JUC并发编程2(高并发,AQS)
JUC AQS核心 当有线程想获取锁时,其中一个线程使用CAS的将state变为1,将加锁线程设为自己。当其他线程来竞争锁时会,判断state是不是0,不是自己就把自己放入阻塞队列种(这个阻塞队列是用双向链表实现)&am…...
Golang 为什么需要用反射
本质上是可以动态获取程序运行时的变量(类型) 比如现在我想实现一个通用的db插入函数,支持我传入所有类型的struct,每一种类型的struct是一个单独的表,以struct的名称作为表名,然后插入到不同的表中。 pa…...

【Linux的进程篇章 - 进程终止和进程等待的理解】
Linux学习笔记---008 Linux之fork函数、进程终止和等待的理解1、fork函数1.1、什么是fork?1.2、fork的功能介绍1.3、fork函数返回值的理解1.4、fork函数的总结 2、进程的终止2.1、终止是在做什么?2.2、进程终止的3种情况 3、进程的终止3.1、进程终止的三种情况3.2、…...
《策略模式(极简c++)》
本文章属于专栏- 概述 - 《设计模式(极简c版)》-CSDN博客 本章简要说明适配器模式。本文分为模式说明、本质思想、实践建议、代码示例四个部分。 模式说明 方案:策略模式是一种行为设计模式,它定义了一系列算法,将每…...
Python向文件里写入数据
直接上代码 name "测试" data name.encode("utf-8")# w特点:文件不存在则创建文件并在打开前清空 f open("db.txt", mode"wb")f.write(data)f.close()可以在 db.txt 文件里看到一句话 测试name "Testing" …...

【网站项目】校园订餐小程序
🙊作者简介:拥有多年开发工作经验,分享技术代码帮助学生学习,独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。🌹赠送计算机毕业设计600个选题excel文件,帮助大学选题。赠送开题报告模板ÿ…...
vue-指令v-for
<!DOCTYPE html> <html lang"en"> <head> <meta charset"UTF-8"> <meta name"viewport" content"widthdevice-width, initial-scale1.0"> <title>vue-指令v-for</title> </head> …...

Python项目1 外星人入侵_外星人
在本章中,我们将在游戏《外星人入侵》中添加外星人。首先,我们在屏幕上边缘附近添加一个外星人,然后生成一群外星人。我们让这群外星人向两边和下面移 动,并删除被子弹击中的外星人。最后,我们将显示玩家拥有的飞船数量…...

导入项目运行后,报错java: Cannot find JDK ‘XX‘ for module ‘XX‘
解决方案: 1、删除.idea和.iml文件 2、右击此module,点击 Open Module Settings 在 Module SDK 中选择所安装的java版本后,点击右下角 Apply,会再生成.idea文件; 那.iml文件呢?操作步骤: 按两下…...
JS rgb,hex颜色值转换
颜色值转化 rgb颜色值转换为hex颜色值(rgb>hex) hex颜色值转换为rgb颜色值(hex>rgb) 代码: const hex2Rgb (hex) > {return rgb(${parseInt(hex.slice(1, 3), 16)},${parseInt(hex.slice(3, 5), 16)},${p…...

Linux| Awk 中“next”命令奇用
简介 本文[1]介绍了在Linux中使用Awk的next命令来跳过剩余的模式和表达式,读取下一行输入的方法。 next命令 在 Awk 系列教程中,本文要讲解如何使用 next 命令。这个命令能让 Awk 跳过所有你已经设置的其他模式和表达式,直接读取下一行数据。…...

基于Springboot的箱包存储系统(有报告)。Javaee项目,springboot项目。
演示视频: 基于Springboot的箱包存储系统(有报告)。Javaee项目,springboot项目。 项目介绍: 采用M(model)V(view)C(controller)三层体系结构&…...

JavaScript_语法--变量
1.4 变量 变量:一小块存储数据的内存空间 Java语言是强类型语言,而JavaScript是弱类型的语言 强类型: 在开辟变量存储空间时,定义了空间将来存储的数据的数据类型。只能存储固定类型的数据 弱类型: 在开辟变量存储空间…...
P1843 奶牛晒衣服
题目背景 熊大妈决定给每个牛宝宝都穿上可爱的婴儿装 。但是由于衣服很湿,为牛宝宝晒衣服就成了很不爽的事情。于是,熊大妈请你(奶牛)帮助她完成这个重任。 题目描述 一件衣服在自然条件下用一秒的时间可以晒干 a 点湿度。抠门…...

功能强大:JMeter 常用插件全解析
JMeter 作为一个开源的接口性能测试工具,其本身的小巧和灵活性给了测试人员很大的帮助,但其本身作为一个开源工具,相比于一些商业工具(比如 LoadRunner),在功能的全面性上就稍显不足。这篇博客,…...

vulhub之fastjson篇-1.2.27-rce
一、启动环境 虚拟机:kali靶机:192.168.125.130/172.19.0.1(docker地址:172.19.0.2) 虚拟机:kali攻击机:192.168.125.130/172.19.0.1 本地MAC:172.XX.XX.XX 启动 fastjson 反序列化导致任意命令执行漏洞 环境 1.进入 vulhub 的 Fastjson 1.2.47 路径 cd /../../vulhub/fa…...

基于springboot实现教师工作量管理系统项目【项目源码+论文说明】计算机毕业设计
基于springboot实现教师工作量管理系统演示 摘要 随着信息技术在管理上越来越深入而广泛的应用,管理信息系统的实施在技术上已逐步成熟。本文介绍了教师工作量管理系统的开发全过程。通过分析教师工作量管理系统管理的不足,创建了一个计算机管理教师工作…...

[StartingPoint][Tier1]Crocodile
Task 1 What Nmap scanning switch employs the use of default scripts during a scan? (哪些 Nmap 扫描开关在扫描期间使用默认脚本?) -sC Task 2 What service version is found to be running on port 21? 发现端口 21 上运行的服务版本是什么?…...

【Qt】:常用控件(四:显示类控件)
常用控件 一.Lable二.LCD Number 一.Lable QLabel 可以⽤来显⽰⽂本和图⽚. 代码⽰例:显⽰不同格式的⽂本 代码⽰例:显⽰图⽚ 此时,如果拖动窗⼝⼤⼩,可以看到图⽚并不会随着窗⼝⼤⼩的改变⽽同步变化 为了解决这个问题,可以在Widget中重写resizeEvent函数。当用户把窗口从A拖…...

gradio简单搭建——关键词简单筛选【2024-4-11优化】
gradio简单搭建——关键词简单筛选[2024-4-11 优化] 新的思路:标签自动标注界面搭建优化数据处理与生成过程交互界面展示 新的思路:标签自动标注 针对通过关键词,在文本数据中体现出主体的工作类型这一任务,这里使用展示工具grad…...

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析
1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具,该工具基于TUN接口实现其功能,利用反向TCP/TLS连接建立一条隐蔽的通信信道,支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式,适应复杂网…...
HTML 语义化
目录 HTML 语义化HTML5 新特性HTML 语义化的好处语义化标签的使用场景最佳实践 HTML 语义化 HTML5 新特性 标准答案: 语义化标签: <header>:页头<nav>:导航<main>:主要内容<article>&#x…...
CVPR 2025 MIMO: 支持视觉指代和像素grounding 的医学视觉语言模型
CVPR 2025 | MIMO:支持视觉指代和像素对齐的医学视觉语言模型 论文信息 标题:MIMO: A medical vision language model with visual referring multimodal input and pixel grounding multimodal output作者:Yanyuan Chen, Dexuan Xu, Yu Hu…...

[ICLR 2022]How Much Can CLIP Benefit Vision-and-Language Tasks?
论文网址:pdf 英文是纯手打的!论文原文的summarizing and paraphrasing。可能会出现难以避免的拼写错误和语法错误,若有发现欢迎评论指正!文章偏向于笔记,谨慎食用 目录 1. 心得 2. 论文逐段精读 2.1. Abstract 2…...
【HarmonyOS 5 开发速记】如何获取用户信息(头像/昵称/手机号)
1.获取 authorizationCode: 2.利用 authorizationCode 获取 accessToken:文档中心 3.获取手机:文档中心 4.获取昵称头像:文档中心 首先创建 request 若要获取手机号,scope必填 phone,permissions 必填 …...
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 __…...

AirSim/Cosys-AirSim 游戏开发(四)外部固定位置监控相机
这个博客介绍了如何通过 settings.json 文件添加一个无人机外的 固定位置监控相机,因为在使用过程中发现 Airsim 对外部监控相机的描述模糊,而 Cosys-Airsim 在官方文档中没有提供外部监控相机设置,最后在源码示例中找到了,所以感…...

脑机新手指南(七):OpenBCI_GUI:从环境搭建到数据可视化(上)
一、OpenBCI_GUI 项目概述 (一)项目背景与目标 OpenBCI 是一个开源的脑电信号采集硬件平台,其配套的 OpenBCI_GUI 则是专为该硬件设计的图形化界面工具。对于研究人员、开发者和学生而言,首次接触 OpenBCI 设备时,往…...

关于easyexcel动态下拉选问题处理
前些日子突然碰到一个问题,说是客户的导入文件模版想支持部分导入内容的下拉选,于是我就找了easyexcel官网寻找解决方案,并没有找到合适的方案,没办法只能自己动手并分享出来,针对Java生成Excel下拉菜单时因选项过多导…...

【C++】纯虚函数类外可以写实现吗?
1. 答案 先说答案,可以。 2.代码测试 .h头文件 #include <iostream> #include <string>// 抽象基类 class AbstractBase { public:AbstractBase() default;virtual ~AbstractBase() default; // 默认析构函数public:virtual int PureVirtualFunct…...