【JavaEE】 多线程-初阶
多线程-初阶
1. 认识线程
1.1 概念
1) 线程是什么
-
多个线程组成了一个进程,线程好比是一跟光纤中的一个玻璃丝,进程是整根光纤。
-
一个进程中的线程共享这个进程中的资源(内存、硬盘)
2) 为什么需要线程
单核CPU发展出现瓶颈,想要再提高算力,只能增加CPU个数,并发编程就是利用多核CPU的绝佳方式.
使用进程也可以实现并发编程,只是进程重量大,创建销毁消耗资源多, 所以更好的方式是使用线程进行并发编程.
3) 线程和进程的区别
- 线程包含于进程
- 每个进程至少有一个线程, 即main线程(main thread)
- 进程之间互不干扰, 但是线程之间耦合度高(一个线程出现问题, 其他线程也会崩溃)
- 进程是系统分配资源的最小单位, 线程是系统调度的最小单位
4) Java中线程 和 操作系统线程 的关系
Java中线程是对于操作系统线程的封装和抽象.
1.2 第一个多线程程序
public class Main {public static void main(String[] args) {Runnable r1 = new Runnable() {@Overridepublic void run() {while (true) {System.out.println("hello thread1.");try {Thread.sleep(2000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}};Thread t1 = new Thread(r1);Runnable r2 = new Runnable() {@Overridepublic void run() {while (true) {System.out.println("hello thread2.");try {Thread.sleep(2000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}};Thread t2 = new Thread(r2);t1.start();t2.start();}
}
运行结果:
hello thread1.
hello thread2.
hello thread1.
hello thread2.
hello thread1.
hello thread2.
hello thread2.
hello thread1.
hello thread1.
hello thread2.
1.3 创建线程
1) 继承Thread
class MyThread extends Thread{public void run() {System.out.println("继承Thread得到");}
}
public class Demo1 {public static void main(String[] args) {MyThread t1 = new MyThread();t1.start();}
}
运行结果:
继承Thread得到
2) 实现Runnable接口
class MyRunnable implements Runnable {@Overridepublic void run() {System.out.println("实现Runnable接口得到");}
}
public class Demo2 {public static void main(String[] args) {MyRunnable r1 = new MyRunnable();Thread t1 = new Thread(r1);t1.start();}
}
运行结果:
实现Runnable接口得到
继承Thread和实现Runnable接口的this指代的对象不同, 前者直接指代这个线程, 后者指代接口, 想要指代线程需要使用Thread.currentThread().
4) 使用匿名内部类
public class Demo3 {public static void main(String[] args) {Thread t1 = new Thread(new Runnable() {@Overridepublic void run() {System.out.println("使用Thread匿名内部类,直接传参new Runnable接口得到");}});Thread t2 = new Thread() {public void run() {System.out.println("使用Thread匿名内部类,直接重写run方法得到");}};t1.start();t2.start();}
}
运行结果:
使用Thread匿名内部类,直接传参new Runnable接口得到
使用Thread匿名内部类,直接重写run方法得到
5) 使用lamda表达式
public class Demo4 {public static void main(String[] args) {Thread t = new Thread(()-> {// 不需要重写run,lamda表达式就相当于是run方法System.out.println("lamda表达式创建得到");});t.start();}
}
运行结果:
lamda表达式创建得到
1.4 使用多线程编程可以增加程序的运行速度
但是可能导致程序线程不安全, 需要合理加锁.
2. Thread类常见方法
2.1 Thread常见构造方法
构造方法名 | 说明 |
---|---|
Thread() | 普通构造(仅分配空间) |
Thread(Runnable) | 根据所给的run()构造对象 |
Thread(String) | 为将构造出的线程进行命名 |
Thread(Runnable, String) | 根据run()创建对象并命名 |
命名主要是为了方便调试.
2.2 Thread常见属性
方法名 | 作用 |
---|---|
start() | 创建线程并运行 |
getId() | 返回线程的Id (这个Id不同于操作系统未进程分配的Id,也不是PCB中的Id,仅仅是JVM分配的Id) |
getName() | 返回线程名字 |
getPriority() | 返回优先级 |
getState() | 返回线程目前的状态(NEW, RUNNABLE, WAITING, TIMED_WAITING,BLOCKED,TERMINATED) |
isDaemon() | 判断是否为后台进程(后台进程不决定一个线程的存亡,只有前台进程才决定) |
isAlive() | 判断是否存活 |
isInterrupted() | 判断是否被中断 |
2.3 让一个Thread跑起来
使用start()方法即可使其开始运行.
之前写过的run方法, 只是为这个线程规定要怎么做, 只有start方法才能启动线程.
2.3.1 start 和 run 的区别
start会调用系统api进行创建线程
run只是一个普通的方法,告诉线程的执行逻辑,不会创建线程
2.4 中断一个线程
有两种方式:
- 设置一个记号,
线程A
和线程B
共享这个记号, 两个线程约定一个在其为true
时工作, 一个在其为false
时工作, 此时如果在A中对于这个记号进行更改, 那就能够使得B停止工作.
public class Demo5 {// 设置共同变量public static boolean flag = true;public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(()->{System.out.println("t1");// 在第一个线程执行完后暂停3秒try {Thread.sleep(3000);} catch (InterruptedException e) {throw new RuntimeException(e);}// 3秒后,设置共同变量为falseflag = false;});Thread t2 = new Thread(()->{while (flag == true) {System.out.println("t2");}});t1.start();// 在第二个线程执行前暂停2秒,让t1线程运行2秒Thread.sleep(2000);// 意味着t2只能执行1秒t2.start();}
}
运行结果:
t1
(等待3秒)
t2
t2
...
t2
t2
在3秒后,t1线程将共享变量修改为false, 所以t2被中断.
- 调用interrupt()进行通知
public class Demo6 {public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(()->{while (!Thread.interrupted()) {System.out.println("t1尚未被中断");try {Thread.sleep(100);} catch (InterruptedException e) {System.out.println("t1收到中断信号");throw new RuntimeException(e);}break;}});System.out.println(t1.getState());t1.start();System.out.println(t1.getState());// 暂停2秒后进行中断Thread.sleep(1);t1.interrupt();}
}
运行结果:
NEW
RUNNABLE
t1尚未被中断
t1收到中断信号
Exception in thread "Thread-0" java.lang.RuntimeException: java.lang.InterruptedException: sleep interruptedat Demo6.lambda$main$0(Demo6.java:10)at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.InterruptedException: sleep interruptedat java.lang.Thread.sleep(Native Method)at Demo6.lambda$main$0(Demo6.java:7)... 1 more
interrupted() 和 currentThread().isInterrupted() 截然不同:
方法名 | 说明 |
---|---|
interrupted() | 查看当前线程是否被中断, 清除标记为false |
currentThread().isInterrupted() | 查看当前线程是否中断,仅作判断, 不清除标记 |
演示:
public class Demo7 {public static void main(String[] args) {Thread t1 = new Thread(()->{for (int i = 0; i < 10; i++) {//System.out.println(Thread.interrupted());System.out.println(Thread.currentThread().isInterrupted());}});t1.start();t1.interrupt();} }运行结果: true true true true true true true true true true //(这种方法不清除中断标记, 仅作判断)
public class Demo7 {public static void main(String[] args) {Thread t1 = new Thread(()->{for (int i = 0; i < 10; i++) {System.out.println(Thread.interrupted());}});t1.start();t1.interrupt();} }运行结果: true false false false false false false false false //(这种方法清除中断标记, 恢复为未被中断状态)
2.5 等待一个线程
线程执行有先后顺序的时候**(线程A的执行需要依赖于线程B的执行结果), **那就需要使用join()方法, 这个方法能够保护当前的线程执行完毕后,其他线程才会去执行.
// Press Shift twice to open the Search Everywhere dialog and type `show whitespaces`,
// then press Enter. You can now see whitespace characters in your code.
public class Main {public static int count = 0;// 1public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(()-> {System.out.println("t11");for (int i = 0; i < 50000; i++) {count++;}System.out.println("t11");});Thread t2 = new Thread(()-> {System.out.println("t21");for (int i = 0; i < 50000; i++) {count++;}System.out.println("t21");});t1.start();t1.join();t2.start();Thread.sleep(100);System.out.println(count);}
}
在都对count进行++五万次的操作中,可以不加锁,也能使得count得到预期值的方法就是让t2在t1执行结束之后才启动,这样两个线程都能完成自己的任务,得到预期count。
2.5.1 方法中不能够加public、static等修饰词
访问局部变量的过程:对象-> 方法->局部变量。访问局部变量就已经有了访问权限的设定了。由此加修饰符也成了摆设。
对应static来说,因为static只能修饰成员变量和成员方法,在局部变量中用static修饰,又不能直接被类调用。
2.6 获取当前线程的引用
使用 Thread.currentThread();
进行获取。
2.7 休眠线程
使用 Thread.sleep(long mills)
实现。
在线程内部需要捕获异常,在方法中使用需要抛出异常。
Thread t2 = new Thread(()-> {System.out.println("t21");for (int i = 0; i < 50000; i++) {count++;}// 捕获异常try {Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}
}, "t2");
// 抛出异常
public static void main(String[] args) throws InterruptedException {
3. 线程的状态
3.1 线程的所有状态
NEW: 安排了工作, 还未开始行动
RUNNABLE: 可工作的. 又可以分成正在工作中和即将开始工作.
BLOCKED: 这几个都表示排队等着其他事情
WAITING: 这几个都表示排队等着其他事情
TIMED_WAITING: 这几个都表示排队等着其他事情
TERMINATED: 工作完成了
使用isAlive()方法可以观察线程是否存活。
使用yield()方法会使线程重新排队。
4. 线程不安全
4.1 线程不安全发生的时机
在两个线程进行对于同一个变量进行修改的时候会出现线程不安全的问题。
4.2 线程不安全发生的原因
-
指令非原子性
即使是“++”这个操作,仅有一条语句,也是由3条微指令构成的:
-
从内存中读出count到寄存器
-
在寄存器中完成++
-
++后的值放进寄存器。
在多个线程的这三个操作如果相互穿插进行,那么就可能会读入”脏值“。
-
-
内存可见性
内存可见性,一个线程对共享变量值的修改,能够及时地被其他线程看到.
对于多次重复的读入同一个数据,编译器会对其进行优化,直接在寄存器中使用这份数据的拷贝值,不再从内存中进行读取,对这个变量的修改操作也都是在这个拷贝值身上完成,在这个线程使用完此变量后才会将最终值写进内存。
这种方式对于单线程来说是一种优化,简便了数据的读取操作,但是对于多线程来说,如果在线程A频繁修改变量count的同时,
线程B需要对count进行修改,那么就会读到“脏值”。
4.2.1 解决内存可见性问题
使用volatile
关键字,忽略编译器对其的优化。
5. synchronized 关键字——解决线程不安全问题
synchronized 会将其所在的代码块进行加锁。
5.1 synchronized 特性
1) 互斥
如果说一个代码块相当于是一间房,那么一个synchronized就相当于是给这个房间进行上锁,其他人想进去必须要等到里面的人把锁打开,两个人进行争夺房间的使用权的过程也称为“锁竞争”。
锁的作用就是让不同的线程拥有同一个对象的锁的时候,只有执行顺序靠前的线程能够正常运行,后面的线程需要等待前面的线程释放锁以后才能继续正常运行。
2)刷新内存
底层实现:
synchronized的底层是使用操作系统的mutex lock实现的.
synchronized工作过程本质上是通过获取一个安全的空间来进行保证操作原子性
的:
-
获得互斥锁
-
从主内存拷贝变量的最新副本到工作的内存 3.
-
执行代码
-
将更改后的共享变量的值刷新到主内存
-
释放互斥锁
3) 可重入性
public static final Object locker = new Object();synchronized (locker) {synchronized (locker) {}
}
在对于一个对象上同一把锁两次的时候,理论上来说会产生“死锁”现象。
因为一个第二把锁所在的代码块执行的前提是第一把锁释放,但是第一把锁释放的条件是后序的代码块执行完,形成闭环,造成“死锁”。
死锁的成因
1)互斥使用:同一把锁的不同线程同一时间只有一个能够运行
2)不可抢占:后面的线程只能等前面的将锁释放后才能运行
3)循环等待:在A阻塞等待B释放锁的时候,B在等待A释放锁
4)请求保持:一个线程尝试获取多把锁(线程A在已经被锁1加上的情况下获取一个已经被占用的锁2,那么锁1不会被释放)
1和2都是锁的基本特性,3和4是代码结构,当同时满足以上四点的时候才会发生死锁。
5.2 synchronized 使用示例
1)给普通方法上锁
synchronized public void method1() {}
2)给静态方法上锁
synchronized public static void method1() {}
3)给代码块上锁
给当前对象上锁
//3
public void method2() {synchronized (this) {}
}
给类对象上锁
//4
public void method3() {synchronized (Demo2.class) {}
}
其中,3和4等价。
5.3 Java 标准库中的线程安全类
不安全的:ArrayList 、LinkedList、 HashMap、 TreeMap、 HashSet、 TreeSet、 StringBuilder
安全的:Vector (不推荐使用)、 HashTable (不推荐使用) 、ConcurrentHashMap、 StringBuffer
6. volatile关键字
6.1 volatile能够保证内存可见性
内存可见性,一个线程对共享变量值的修改,能够及时地被其他线程看到.
import java.util.Scanner;// volatile的作用
public class Demo3 {//public volatile static int isQuit = 0;public static int isQuit = 0;public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(()-> {while (isQuit == 0) {}System.out.println("t1退出");});t1.start();Thread.sleep(1000);Thread t2 = new Thread(()-> {System.out.println("请输入");Scanner scanner = new Scanner(System.in);isQuit = scanner.nextInt();});t2.start();Thread.sleep(1000);System.out.println(t1.getState());}
}
由于编译器的优化, t2对于isQuit变量进行修改并不影响t1线程中看到的isQuit变量是0, 这就叫做内存不可见.
但是如果加上volatile, 那么编译器会保证内存的可见性, 放弃优化.(所以会将代码的运行效率降低)
volatile的工作过程:
- 将内存中的数据放进寄存器
- 线程对于数据进行修改
- 将数据写回内存
如果是读取:
- 读取最新值进入工作内存
- 从工作内存中读取volatile变量的副本
6.2 volatile不能保证操作的原子性
volatile虽然一次性将数据读取到工作内存, 待其写完后又放回主内存, 但是在写的过程中, 如果其他线程也对同一个变量进行写入, 这将是合法的, 并且存在线程安全问题.
// 线程安全问题
public class Main {// 加上volatile并不能够得到预期的count值public static volatile int count = 0;// 1public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(()-> {for (int i = 0; i < 50000; i++) {count++;}try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}});Thread t2 = new Thread(()-> {for (int i = 0; i < 50000; i++) {count++;}}, "t2");t1.start();t2.start();Thread.sleep(1000);System.out.println(count);}
}
结果:
75377
6.3 但是synchronized可以保证内存可见性和原子性
相关文章:

【JavaEE】 多线程-初阶
多线程-初阶 1. 认识线程 1.1 概念 1) 线程是什么 多个线程组成了一个进程,线程好比是一跟光纤中的一个玻璃丝,进程是整根光纤。 一个进程中的线程共享这个进程中的资源(内存、硬盘) 2) 为什么需要线程 单核CPU发展出现瓶颈…...

小程序OCR身份证识别
使用两种OCR识别:小程序和腾讯云 1.基于微信小程序OCR插件实现身份证拍照、上传并OCR识别的示例: 首先,在小程序中添加身份证拍照的功能,可以使用wx.chooseImage()选择照片并使用wx.uploadFile()上传,代码如下&#…...

【算法学习】归并算法Merge Sort总结
归并排序思路简单,速度仅次于快速排序,为稳定排序算法,一般用于对总体无序,但是各子项相对有序的数列。 1. 基本思想 归并排序使用分治思想,分治模式下每一层递归有三个步骤: 分解(divide)&a…...

Swager如何使用
Swager是一个API文档自动生成工具,可以用于生成API接口文档,供开发者和用户查看和使用。它可以通过描述API接口的规范,自动生成API文档,使得API接口的发布和使用变得更加简单和规范。 下面是使用Swagger的步骤: 首先…...

DHorse v1.4.2 发布,基于 k8s 的发布平台
版本说明 优化特性 在集群列表增加集群版本;修改Jvm的GC指标名; 解决问题 解决shell脚本换行符的问题;解决部署历史列表页,环境名展示错误的问题;解决指标收集功能的异常; 升级指南 升级指南 DHorse…...

Java使用JJWT令牌
最近在B站大学学习Java开发,刚好学到登入验证,在使用JJWT令牌时踩了一些坑,在这里把代码和依赖给出,希望后来者得以借鉴。 依赖 <dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-api&l…...

“第四十四天”
这道题也不是难,但可能会忽略一种情况,当最大小出现在首位的时候,那个时候如果进行交换的话,大小值可能出现覆盖的情况,最终导致丢失最大值或者最小值,比如最大值 10 在第一位,最小值 0 随意&am…...

Unity Mono和.Net平台浮点算法的区别
static void TestFloat(){{//float speed2.0f/20;float speed 0.1f;float distance 2.0f;long needTime (long)(distance / speed);Log.Debug($"needTime{needTime}"); #if UNITY_EDITORif (needTime ! 19) #elseif (needTime ! 20)//.Net服务器和安卓手机 #endif…...

【SA8295P 源码分析 (二)】64 - QNX 与 Android GVM 显示 Dump 图片方法汇总
【SA8295P 源码分析】64 - QNX 与 Android GVM 显示 Dump 图片方法汇总 一、QNX侧1.1 surfacedump 功能1.2 screenshot 功能二、Android GVM 侧2.1 screencap -p 导出 PNG 图片2.2 screencap 不加 -p 参数,导出 RGB32 图片2.3 dumpsys SurfaceFlinger --display-id 方法系列文…...

shell命令以及运行原理和lLinux权限
shell命令以及运行原理 什么是shell shell是操作系统的外壳程序统称,我们是通过shell去和操作系统沟通的。 从技术角度,shell最简单的定义就是命令行解释器,主要包含两个功能: 将使用者的命令翻译给核心处理 将核心的处理结果…...

斯坦福JSKarel编程机器人使用介绍
斯坦福JSKarel编程机器人使用介绍 为了避免被编程语言固有的复杂性所困扰,有一个被称为卡雷尔(Karel)机器人的微型世界(microworld)的简化环境,可以让编程初学者从中学习理解编程的基本概念,而…...

SpringBoot中pom.xml不引入依赖, 怎么使用parent父项目的依赖
在Spring Boot项目中,如果你想使用父项目的依赖,而不想在pom.xml中显式引入依赖,你可以使用Maven的继承机制。 首先,确保你的Spring Boot项目是一个子项目,即它继承自一个父项目。要实现这一点,在pom.xml文…...

基于vue3+ts5+vue-router4+pinia2的PC端项目搭建教程
导语:在日常开发中,有时候会在项目中引入 ts 来解决一些 js 的问题,下面就简单介绍一下如何使用 vue3tsrouterpinia 来搭建一个项目。 目录 简介创建安装配置实战 简介 vue3 目前是常用的 vue 版本,提供了组合式 API 以及一些新…...

6个无版权、免费、高清图片素材库
找免费无版权图片素材,就上这6个网站,超高质量,可商用,赶紧收藏! 1、菜鸟图库 https://www.sucai999.com/pic.html?vNTYwNDUx 网站主要为新手设计师提供免费素材,这些素材的质量都很高,类别也…...

什么是响应式设计?响应式设计的基本原理是什么?如何兼容低版本的 IE?
什么是响应式设计: 响应式设计(Responsive Design)是一种Web设计和开发方法,旨在使网站在不同设备和屏幕尺寸上都能提供一致的用户体验。响应式设计的目标是适应多种终端,包括桌面计算机、笔记本电脑、平板电脑和移动设备&#x…...

LeetCode 2906. 构造乘积矩阵【前后缀分解,数组】中等
本文属于「征服LeetCode」系列文章之一,这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁,本系列将至少持续到刷完所有无锁题之日为止;由于LeetCode还在不断地创建新题,本系列的终止日期可能是永远。在这一系列刷题文章…...

vue3+koa+axios实现前后端通信
vue3koaaxios实现前后端通信 写了一个小demo来实现前后端通信,涉及跨域问题,非常简单可以给大家平时开发的时候参考 服务端: 目录结构如下: router index.js // router的入口文件 // 引入路由 const Router require("koa-router&quo…...

Required MultipartFile parameter ‘file‘ is not present
出现这个原因我们首先想到的是加一个RequestParam("file"),但是还有可能的原因是因为我们的名字有错误 <span class"input-group-addon must">模板上传 </span> <input id"uploadFileUpdate" name"importFileU…...

vue3后台管理系统之layout组件的搭建
1.1静态布局 <template><div class"layout_container"><!-- 左侧导航 --><div class"layout_slider"></div><!-- 顶部导航 --><div class"layout_tabbar"></div><!-- 内容展示区 --><…...

Minio 文件上传(后端处理同文件判断,同一文件秒传)
记录minio 文件上传 MinIO提供多个语言版本SDK的支持,下边找到java版本的文档: 地址:https://docs.min.io/docs/java-client-quickstart-guide.html maven依赖如下: XML <dependency><groupId>io.minio</groupId…...

模拟IIC通讯协议(stm32)(硬件iic后面在补)
一、IIC基础知识总结。 1、IIC通讯需要两条线就可以,SCL、SDA。 2、IIC的数据传输的速率,不同的ic是不同的,根据电平维持的延时函数的时间来确定IIC数据传输的速率. 3、IIC的延时函数可以使用延时函数,延时函数一般使用系统滴答时…...

使用注解读取properties配置文件
文章目录 1、背景2、注解方式2.1 PropertySource 、 ConfigurationProperties2.2 读取properties中全部字段值ConfigurationProperties2.3 读取properties中部分字段值:value("${自定义key}") 1、背景 服务中使用到了redis,需要配置redis连接…...

Python---练习:求世界杯小组赛的总成绩(涉及:布尔类型转换为整型)
案例 世界杯案例 需求: 世界杯案例,世界杯小组赛的比赛规则是我们的球队与其他三支球队进行比赛,然后根据总成绩(积分)确定出线资格。小组赛球队实力已知(提示用户输入各球队实力),我们通过一个数字表示。如果我们赢…...

vue3学习源码笔记(小白入门系列)------KeepAlive 原理
目录 说明组件是如何被缓存的,什么时候被激活对于KeepAlive 中组件 如何完成激活的对于KeepAlive 中组件 如何完成休眠的 总结 说明 Vue 内置了 KeepAlive 组件,实现缓存多个组件实例切换时,完成对卸载组件实例的缓存,从而使得组…...

边写代码边学习之mlflow
1. 简介 MLflow 是一个多功能、可扩展的开源平台,用于管理整个机器学习生命周期的工作流程和工件。 它与许多流行的 ML 库内置集成,但可以与任何库、算法或部署工具一起使用。 它被设计为可扩展的,因此您可以编写插件来支持新的工作流程、库和…...

基于吉萨金字塔建造优化的BP神经网络(分类应用) - 附代码
基于吉萨金字塔建造优化的BP神经网络(分类应用) - 附代码 文章目录 基于吉萨金字塔建造优化的BP神经网络(分类应用) - 附代码1.鸢尾花iris数据介绍2.数据集整理3.吉萨金字塔建造优化BP神经网络3.1 BP神经网络参数设置3.2 吉萨金字…...

axios的post请求所有传参方式
Axios支持多种方式来传递参数给POST请求。以下是一些常见的方式: 作为请求体: 你可以将参数作为请求体的一部分,通常用于发送表单数据或JSON数据。例如: const data { key1: value1, key2: value2 }; axios.post(/api/endpoint, …...

【c++】向webrtc学比较2: IsNewerSequenceNumber 用于NackTracker及测试
LatestSequenceNumber inline uint16_t LatestSequenceNumber(uint16_t sequence_number1,uint16_t sequence_number2) {return IsNewerSequenceNumber(sequence_number1, sequence_number2)? sequence_number1: sequen...

PRCV 2023:语言模型与视觉生态如何协同?合合信息瞄准“多模态”技术
近期,2023年中国模式识别与计算机视觉大会(PRCV)在厦门成功举行。大会由中国计算机学会(CCF)、中国自动化学会(CAA)、中国图象图形学学会(CSIG)和中国人工智能学会&#…...

深度学习硬件配置推荐(kaggle学习)
目录 1. 基础推荐2. GPU显存与内存是一个1:4的配比?3. deep learning 入门和kaggle比赛4. 有些 Kaggle 比赛数据集很大,可能需要更多的 GPU 显存,请推荐显存4. GDDR6和HBM25. HDD 或 SATA SSD 1. 基础推荐 假设您作为一个深度学习入门学者的…...