二、并发编程的三大特性
文章目录
- 并发编程的三大特性
- 1、原子性
- 什么是并发编程的原子性?
- 保证并发编程的原子性
- synchronized
- CAS
- Lock锁
- ThreadLocal
- 2、可见性
- 什么是可见性?
- 解决可见性的方式
- volatile
- synchronized
- Lock
- final
- 3、有序性
- 什么是有序性?
- as-if-serial
- happens-before
- volatile
并发编程的三大特性
原子性、可见性、有序性。
1、原子性
什么是并发编程的原子性?
JMM(Java Memory Model)。不同的硬件和不同的操作系统在内存上的操作有一定差异的。 Java为了解决相同代码在不同操作系统上出现的各种问题,用JMM屏蔽掉各种硬件和操作系统带来的差异。
让Java的并发编程可以做到跨平台。 JMM规定所有变量都会存储在主内存中,在操作的时候,需要从主内存中复制一份到线程内存(CPU内存),在线程内部做计算。然后再写回主内存中(不一定)。
原子性的定义:原子性指一个操作是不可分割的,不可中断的,一个线程在执行时,另一个线程不会影响到他。
并发编程的原子性用代码阐述:
private static int count;public static void increment() {try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}count++;}public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(() -> {for (int i = 0; i < 100; i++) {increment();}});Thread t2 = new Thread(() -> {for (int i = 0; i < 100; i++) {increment();}});t1.start();t2.start();t1.join();t2.join();System.out.println(count);}
当前程序:多线程操作共享数据时,预期的结果,与最终的结果不符。
原子性:多线程操作临界资源,预期的结果与最终结果一致。
通过对这个程序的分析,可以查看出,++的操作,一共分为了三部,首先是线程从主内存拿到数据,保存到CPU的寄存器中,然后在寄存器中进行+1操作,最终将结果写回到主内存当中。
保证并发编程的原子性
synchronized
因为++操作可以从指令中查看到
可以在方法上追加synchronized关键字或者采用同步代码块的形式来保证原子性。
synchronized可以让避免多线程同时操作临街资源,同一时间点,只会有一个线程正在操作临界资源。
CAS
compare and swap也就是比较和交换,他是一条CPU的并发原语。
他在替换内存的某个位置的值时,首先查看内存中的值与预期值是否一致,如果一致,执行替换操作。这个操作是一个原子性操作。
Java中基于Unsafe的类提供了对CAS的操作的方法,JVM会帮助我们将方法实现CAS汇编指令。 但是要清楚CAS只是比较和交换,在获取原值的这个操作上,需要你自己实现。
private static AtomicInteger count = new AtomicInteger(0);public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(() -> {for (int i = 0; i < 100; i++) {count.incrementAndGet();}});Thread t2 = new Thread(() -> {for (int i = 0; i < 100; i++) {count.incrementAndGet();}});t1.start();t2.start();t1.join();t2.join();System.out.println(count);}
Doug Lea在CAS的基础上帮助我们实现了一些原子类,其中就包括现在看到的AtomicInteger,还有其他很多原子类。
CAS的缺点:CAS只能保证对一个变量的操作是原子性的,无法实现对多行代码实现原子性。
CAS带来的问题
- ABA问题:问题如下,可以引入版本号的方式,来解决ABA的问题。Java中提供了一个类在CAS时,针对各个版本追加版本号的操作。 AtomicStampeReference在CAS时,不但会判断原值,还会比较版本信息。
public static void main(String[] args) {AtomicStampedReference<String> reference = new AtomicStampedReference<>("AAA", 1);String oldValue = reference.getReference();int oldVersion = reference.getStamp();boolean b = reference.compareAndSet(oldValue, "B", oldVersion, oldVersion + 1);System.out.println("修改1版本的:" + b);boolean c = reference.compareAndSet("B", "C", 1, 1 + 1);System.out.println("修改2版本的:" + c);}
- 自旋时间过长问题
- 可以指定CAS一共循环多少次,如果超过这个次数,直接失败/或者挂起线程。(自旋锁、 自适应自旋锁)
- 可以在CAS一次失败后,将这个操作暂存起来,后面需要获取结果时,将暂存的操作全部执行,再返回最后的结果。
ABA问题:
- 线程1从内存位置V中取出A
- 线程2从内存位置V中取出A
- 线程2进行了写操作,将B写入内存位置V
- 线程2将A再次写入内存位置V
- 线程1进行CAS操作,发现V中仍然是A,交换成功
尽管线程1的CAS操作成功,但线程1并不知道内存位置V的数据发生过改变。
Lock锁
Lock锁是在JDK1.5由Doug Lea研发的,他的性能相比synchronized在JDK1.5的时期,性能好了很很多,但是在JDK1.6对synchronized优化之后,性能相差不大,但是如果涉及并发比较多时,推荐ReentrantLock锁,性能会更好。
private static int count;private static ReentrantLock lock = new ReentrantLock();public static void increment() {lock.lock();try {count++;try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}} finally {lock.unlock();}}public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(() -> {for (int i = 0; i < 100; i++) {increment();}});Thread t2 = new Thread(() -> {for (int i = 0; i < 100; i++) {increment();}});t1.start();t2.start();t1.join();t2.join();System.out.println(count);}
ReentrantLock可以直接对比synchronized,在功能上来说,都是锁。但是ReentrantLock的功能性相比synchronized更丰富。
ReentrantLock底层是基于AQS实现的,有一个基于CAS维护的state变量来实现锁的操作。
ThreadLocal
Java中的四种引用类型分别是强软弱虚。
User user = new User();
在 Java 中最常见的就是强引用,把一个对象赋给一个引用变量,这个引用变量就是一个强引用。当一个对象被强引用变量引用时,它始终处于可达状态,它是不可能被垃圾回收机制回收的,即使该对象以后永远都不会被用到JVM也不会回收。因此强引用是造成 Java 内存泄漏的主要原因之一。
SoftReference
其次是软引用,对于只有软引用的对象来说,当系统内存足够时它不会被回收,当系统内存空间不足时它会被回收。软引用通常用在对内存敏感的程序中,作为缓存使用。
然后是弱引用,它比软引用的生存期更短,对于只有弱引用的对象来说,只要垃圾回收机制一运行,不管 JVM 的内存空间是否足够,总会回收该对象占用的内存。可以解决内存泄漏问题,ThreadLocal就是基于弱引用解决内存泄漏的问题。
最后是虚引用,它不能单独使用,必须和引用队列联合使用。虚引用的主要作用是跟踪对象被垃圾回收的状态。不过在开发中,我们用的更多的还是强引用。
ThreadLocal保证原子性的方式,是不让多线程去操作临界资源,让每个线程去操作属于自己的数据。
static ThreadLocal tl1 = new ThreadLocal();static ThreadLocal tl2 = new ThreadLocal();public static void main(String[] args) {tl1.set("123");tl2.set("456");Thread t1 = new Thread(() -> {System.out.println("t1:" + tl1.get());System.out.println("t1:" + tl2.get());});t1.start();System.out.println("main:" + tl1.get());System.out.println("main:" + tl2.get());}
ThreadLocal实现原理:
- 每个Thread中都存储着一个成员变量,ThreadLocalMap。
- ThreadLocal本身不存储数据,像是一个工具类,基于ThreadLocal去操作ThreadLocalMap。
- ThreadLocalMap本身就是基于Entry[]实现的,因为一个线程可以绑定多个ThreadLocal,这样一来,可能需要存储多个数据,所以采用Entry[]的形式实现。
- 每一个现有都自己独立的ThreadLocalMap,再基于ThreadLocal对象本身作为key,对value进行存取ThreadLocalMap的key是一个弱引用,弱引用的特点是,即便有弱引用,在GC时,也必须被回收。这里是为了在ThreadLocal对象失去引用后,如果key的引用是强引用,会导致 ThreadLocal对象无法被回收。
ThreadLocal内存泄漏问题:
- 如果ThreadLocal引用丢失,key因为弱引用会被GC回收掉,如果同时线程还没有被回收,就会导致内存泄漏,内* * 存中的value无法被回收,同时也无法被获取到。
只需要在使用完毕ThreadLocal对象之后,及时的调用remove方法,移除Entry即可。
2、可见性
什么是可见性?
可见性问题是基于CPU位置出现的,CPU处理速度非常快,相对CPU来说,去主内存获取数据这个 事情太慢了,CPU就提供了L1,L2,L3的三级缓存,每次去主内存拿完数据后,就会存储到CPU的 三级缓存,每次去三级缓存拿数据,效率肯定会提升。
这就带来了问题,现在CPU都是多核,每个线程的工作内存(CPU三级缓存)都是独立的,会告知每个线程中做修改时,只改自己的工作内存,没有及时的同步到主内存,导致数据不一致问题。
可见性问题的代码逻辑:
private static boolean flag = true;public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(() -> {while (flag) {// ....}System.out.println("t1线程结束");});t1.start();Thread.sleep(10);flag = false;System.out.println("主线程将flag改为false");}
解决可见性的方式
volatile
volatile是一个关键字,用来修饰成员变量。
如果属性被volatile修饰,相当于会告诉CPU,对当前属性的操作,不允许使用CPU的缓存,必须去 和主内存操作。
volatile的内存语义:
- volatile属性被写:当写一个volatile变量,JMM会将当前线程对应的CPU缓存及时的刷新到主内存中。
- volatile属性被读:当读一个volatile变量,JMM会将对应的CPU缓存中的内存设置为无效,必须去主内存中重新读取共享变量。
其实加了volatile就是告知CPU,对当前属性的读写操作,不允许使用CPU缓存,加了volatile修饰的 属性,会在转为汇编之后,追加一个lock的前缀,CPU执行这个指令时,如果带有lock前缀会做两个事情:
- 将当前处理器缓存行的数据写回到主内存。
- 这个写回的数据,在其他的CPU内核的缓存中,直接无效。
总结:volatile就是让CPU每次操作这个数据时,必须立即同步到主内存,以及从主内存读取数据。
private volatile static boolean flag = true;public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(() -> {while (flag) {// ....}System.out.println("t1线程结束");});t1.start();Thread.sleep(10);flag = false;System.out.println("主线程将flag改为false");}
synchronized
synchronized也是可以解决可见性问题的,synchronized的内存语义。
如果涉及到了synchronized的同步代码块或者是同步方法,获取锁资源之后,将内部涉及到的变量从CPU缓存中移除,必须去主内存中重新拿数据,而且在释放锁之后,会立即将CPU缓存中的数据同步到主内存。
private static boolean flag = true;public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(() -> {while (flag) {synchronized (MiTest.class) {//...}System.out.println(111);}System.out.println("t1线程结束");});t1.start();Thread.sleep(10);flag = false;System.out.println("主线程将flag改为false");}
Lock
Lock锁保证可见性的方式和synchronized完全不同,synchronized基于他的内存语义,在获取锁和释放锁时,对CPU缓存做一个同步到主内存的操作。
Lock锁是基于volatile实现的。Lock锁内部再进行加锁和释放锁时,会对一个由volatile修饰的state属性进行加减操作。
如果对volatile修饰的属性进行写操作,CPU会执行带有lock前缀的指令,CPU会将修改的数据,从 CPU缓存立即同步到主内存,同时也会将其他的属性也立即同步到主内存中。还会将其他CPU缓存 行中的这个数据设置为无效,必须重新从主内存中拉取。
private static boolean flag = true;private static Lock lock = new ReentrantLock();public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(() -> {while (flag) {lock.lock();try {//...} finally {lock.unlock();}}System.out.println("t1线程结束");});t1.start();Thread.sleep(10);flag = false;System.out.println("主线程将flag改为false");}
final
final修饰的属性,在运行期间是不允许修改的,这样一来,就间接的保证了可见性,所有多线程读 取final属性,值肯定是一样。
final并不是说每次取数据从主内存读取,他没有这个必要,而且final和volatile是不允许同时修饰一个属性的。
final修饰的内容已经不允许再次被写了,而volatile是保证每次读写数据去主内存读取,并且volatile 会影响一定的性能,就不需要同时修饰。
3、有序性
什么是有序性?
在Java中,.java文件中的内容会被编译,在执行前需要再次转为CPU可以识别的指令,CPU在执行 这些指令时,为了提升执行效率,在不影响最终结果的前提下(满足一些要求),会对指令进行重排。
指令乱序执行的原因,是为了尽可能的发挥CPU的性能。
Java中的程序是乱序执行的。
static int a, b, x, y;public static void main(String[] args) throws InterruptedException {for (int i = 0; i < Integer.MAX_VALUE; i++) {a = 0;b = 0;x = 0;y = 0;Thread t1 = new Thread(() -> {a = 1;x = b;});Thread t2 = new Thread(() -> {b = 1;y = a;});t1.start();t2.start();t1.join();t2.join();if (x == 0 && y == 0) {System.out.println("第" + i + "次,x = " + x + ",y = " + y);}}}
单例模式由于指令重排序可能会出现问题:
线程可能会拿到没有初始化的对象,导致在使用时,可能由于内部属性为默认值,导致出现一些不必
要的问题。
private static volatile MiTest test;private MiTest() {}public static MiTest getInstance() { // Bif (test == null) {synchronized (MiTest.class) {if (test == null) {// A , 开辟空间,test指向地址,初始化test = new MiTest();}}}return test;}
as-if-serial
as-if-serial语义: 不论指定如何重排序,需要保证单线程的程序执行结果是不变的。 而且如果存在依赖的关系,那么也不可以做指令重排。
// 这种情况肯定不能做指令重排序 int i = 0;
i++;
// 这种情况肯定不能做指令重排序 int j = 200;
j * 100;
j + 100;
// 这里即便出现了指令重排,也不可以影响最终的结果,20100
happens-before
具体规则:
- 单线程happen-before原则:在同一个线程中,书写在前面的操作happen-before后面的操作。
- 锁的happen-before原则:同一个锁的unlock操作happen-before此锁的lock操作。
- volatile的happen-before原则:对一个volatile变量的写操作happen-before对此变量的任意操作。
- happen-before的传递性原则:如果A操作 happen-before B操作,B操作happen-before C操作,那么A操作happen-before C操作。
- 线程启动的happen-before原则:同一个线程的start方法happen-before此线程的其它方法。
- 线程中断的happen-before原则:对线程interrupt方法的调用happen-before被中断线程的检测到中断发送的代码。
- 线程终结的happen-before原则:线程中的所有操作都happen-before线程的终止检测。
- 对象创建的happen-before原则:一个对象的初始化完成先于他的finalize方法调用。
JMM只有在不出现上述8中情况时,才不会触发指令重排效果。不需要过分的关注happens-before原则,只需要可以写出线程安全的代码就可以了。
volatile
如果需要让程序对某一个属性的操作不出现指令重排,除了满足happens-before原则之外,还可以基于volatile修饰属性,从而对这个属性的操作,就不会出现指令重排的问题了。
volatile如何实现的禁止指令重排?
内存屏障概念。将内存屏障看成一条指令。 会在两个操作之间,添加上一道指令,这个指令就可以避免上下执行的其他指令进行重排序。
相关文章:

二、并发编程的三大特性
文章目录并发编程的三大特性1、原子性什么是并发编程的原子性?保证并发编程的原子性synchronizedCASLock锁ThreadLocal2、可见性什么是可见性?解决可见性的方式volatilesynchronizedLockfinal3、有序性什么是有序性?as-if-serialhappens-beforevolatile并发编程的…...

Ubuntu 22.04.2 LTS安装Apollo8.0
本人硬件环境: CPU:Intel Core i7 6700 显卡(GPU):NVIDIA GTX 3080 10G 内存:SAMSUNG DDR4 32GB 硬盘:双SSD系统盘 2T,双系统(windows,ubuntu) 一、安装Ubuntu 22.04…...

提高转化率的 3 个客户引导最佳实践
如果您的试用客户没有转化为付费客户,或者您总体上正在努力解决试用到付费转化率,那么您来对地方了。本文的最终目标是向您展示一些可用于提高自己的激活率和整体试用到付费转化的最佳客户引导实践。SaaS公司目前生活在一个以产品为主导的增长时代。换句…...

【消费战略】解读100个食品品牌丨元气森林 6年百亿的饮品黑马成功之道
元气森林成立于2016年,短短六年时间取得了近百亿营收的奇迹,成为让可口可乐、百事、娃哈哈、农夫山泉等消费巨头都无法忽视的对手。六年的成长堪比行业前辈20多年的积累,从这个角度而言,塔望咨询认为元气森林是成功的,…...

b2b b2c o2o分布式电子商务平台源码 mybatis+spring cloud
鸿鹄云商大型企业分布式互联网电子商务平台,推出PC微信APP云服务的云商平台系统,其中包括B2B、B2C、C2C、O2O、新零售、直播电商等子平台。 分布式、微服务、云架构电子商务平台 java b2b2c o2o 技术解决方案 开发语言: java、j2ee 数据库&am…...

LeetCode104_104. 二叉树的最大深度
LeetCode104_104. 二叉树的最大深度 一、描述 给定一个二叉树,找出其最大深度。 二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。 说明: 叶子节点是指没有子节点的节点。 示例: 给定二叉树 [3,9,20,null,null,15,7], 3/ \9 …...

浏览器跨域问题
跨域问题什么是跨域问题如何解决跨域问题JSONPCORS方式解决跨域使用 Nginx 反向代理使用 WebSocket跨源请求是否能携带Cookie什么是跨域问题 跨域问题指的是不同站点之间,使用 ajax 无法相互调用的问题。跨域问题本质是浏览器的一种保护机制,它的初衷是为…...

面向对象的三特性
继承Java中通过继承,子类可以获取父类的属性和方法,不需要自己去定义即可获取,可以提高代码的复用性;同时,子类如果对继承的方法不满意,可以自己重写方法,进行个性化定制。好处:提高…...

管理者如何给员工沟通绩效
目录 1.沟通基础 2.聊绩效第一部分,心理预期管理 3.聊绩效第二部分,分人沟通 3.1 高绩效者 3.2 中绩效者 3.3 低绩效者 4.注意 1.沟通基础 无论在哪里工作,每个员工都不免会遇到绩效沟通的事情。作为管理层,通过每年的绩效…...

使用Python启动appium
import osimport subprocessimport multiprocessingimport timeimport pytestfrom appium import webdriverfrom selenium.webdriver.support.wait import WebDriverWaitfrom time import sleep# 关于appium的启动# 1、桌面版(咱们现在用的):…...

活动回顾丨研发效能度量线下沙龙圆满举办
2月18日,由跬智信息(Kyligence)联合甄知科技主办的研发效能度量线下沙龙圆满举办。本次沙龙在 Kyligence 上海总部举办,Kyligence 联合创始人兼 CTO 李扬、腾讯 Tech Lead 茹炳晟,以及甄知科技创始人兼 CTO 张礼军在现…...

问题解决篇 | Win11网络连接上了但是无法上网(修改DNS弹出框框“出现问题”,如何通过网络检测确定并修复网络问题)
目录 问题 网络诊断 Win i 打开设置 搜索“查找并修复网络问题”并点击 "远程计算机或设备将不接受连接" 解决办法: Win R,输入 inetcpl.cpl ,点击确定,打开Internet选项 选择“连接” 点击“局域网设置” 三个…...

Go语言进阶与依赖管理-学习笔记
1 语言进阶 1.1 Goroutine 线程:内核态,栈MB级别 协程:用户态,轻量级线程,栈KB级 1.2 CSP 提倡通信实现共享内存 1.3 Channel 创建方法 make(chan 元素类型,缓冲区大小) 无缓冲通道&#x…...

【Mybatis源码分析】datasource配置${}表达式时是如何被解析的?
核心配置中${}表达式配置的解析一、核心配置主体二、核心配置文件中properties是如何被解析的?三、${} 表达式的解析四、总结前提: 核心配置文件是被XMLConfigBuilder 对象进行解析的,configuration 对象是由它父类BaseBuider继承下来的属性…...

网络基础概述
1.计算机网络背景 计算机刚刚发展的时候,是没有网络的,每一台计算机都是相互独立的。后来,人们有了多人协作的需求,人们就想办法把多台计算机用“线”连接起来,实现数据共享。后来,连接到一起的电脑越来…...

微搭使用笔记(四) 通过循环展示组件+json配置生成表单及数据获取
背景及整体思路 上篇文章我们通过微搭提供的数据模型完成了问卷表单页面的创建和数据采集,相对来说除了数据模型配置略显复杂外其他的倒还算方便。 本文我们通过for循环加上json文件配置的方式实现一个通用表单页面,如果更换了表单只需要替换掉json配置…...

做测试5年,靠业务熟悉吃老本,技术短板暴露,30岁被无情辞退...
朋友跟我诉苦,最近他被公司无情辞退了。测试几年,月薪10k,如今已经30了,接下来不知道该怎么办,让我帮他想想办法... 几年下来,也算是公司的骨干成员,不说有功,但一定无过。公司业务…...

Linux系统安装MySQL8.0版本详细教程【亲测有效】
首先官网下载安装包:https://downloads.mysql.com/archives/community/ 一、上传到安装服务器 二、解压 tar -xvf mysql-8.0.31-linux-glibc2.12-x86_64.tar.xz三、移动位置并重新命名 mv mysql-8.0.31-linux-glibc2.12-x86_64 /usr/local/mysql四、创建mysql用户…...

[论文阅读笔记19]SiamMOT: Siamese Multi-Object Tracking
这是CVPR2021的一篇文章, 是利用SOT的一些思想来进行MOT的运动估计. 文章地址: 文章 代码地址: 代码 0. 摘要 本文提出了一个孪生(Siamese)式的MOT网络, 该网络用来估计帧间目标的运动. 为了探究运动估计对多目标跟踪的影响, 本文提出了两种运动建模方式: 显式和隐式. 本文在…...

unix高级编程-fork和execve
fork和vfork vfork是老的实现方法又很多问题 vfork #include <sys/types.h> #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <signal.h> #include <errno.h> #include <sys/stat.…...

Vue3+Ts+Vite开发插件并发布到npm
依赖版本信息如下: "vue": "^3.2.45""typescript": "~4.7.4""vite": "^4.0.0""less": "^4.1.3""terser": "^5.16.4"npm: 8.1.0node: 16.13.0 目标…...

CAN TP层函数介绍
如果想使用CAN TP层函数,首先需要在网络节点或测试节点配置页面的Componets组件一栏添加osek_tp.dll文件。路径为:C:\Program Files\Vector CANoe 15\Exec32 至于节点的CAPL程序内需不需要引用这个dll文件,无所谓,可写可不写。但是如果是其他dll,必须在CAPL程序中引用。为…...

Spring架构篇--2.5 远程通信基础Select 源码篇--window--Select.open()
前言:在Socket通信中使用Select 来对NIO 进行实现,那么它们的实现方式是怎样的呢,本文从 Selector.open() 进行第一步的分析; Selector.open() : Selector 类: public static Selector open() throws IOEx…...

WEB静态交互展示【数据mock】
文章目录背景需求分析实现过程1.爬取原有项目数据2.将数据引入项目3.打包收工后记背景 接到公司一个【离谱】的需求,要求把已有的项目做一个演示版本(静态文件版本);本人觉得前端、后端搞个容器包,一个演示版本不就有…...

(4)C#传智:分支Switch与循环While(第四天)
一、异常捕获 定义:语法无错,程序因某些原因出现的错误,而不能正常运行。 用try-catch进行捕获。哪行代码可能出现异常,你就踹它一脚。 try { 可能会出现异常的代码; ---- …...

Stable-Baselines 3 部分源代码解读 2 on_policy_algorithm.py
Stable-Baselines 3 部分源代码解读 ./common/on_policy_algorithm.py 前言 阅读PPO相关的源码,了解一下标准库是如何建立PPO算法以及各种tricks的,以便于自己的复现。 在Pycharm里面一直跳转,可以看到PPO类是最终继承于基类,也…...

15. Qt中OPenGL的参数传递问题
1. 说明 在OPenGL中,需要使用GLSL语言来编写着色器的函数,在顶点着色器和片段着色器之间需要参数值的传递,且在CPU中的数据也需要传递到顶点着色器中进行使用。本文简单介绍几种参数传递的方式: (本文内容仅个人理解&…...

注意,这本2区SCI期刊最快18天录用,还差一步录用只因犯了这个错
发表案例分享: 2区医学综合类SCI,仅18天录用,录用后28天见刊 2023.02.10 | 见刊 2023.01.13 | Accepted 2023.01.11 | 提交返修稿 2022.12.26 | 提交论文至期刊部系统 录用截图来源:期刊部投稿系统 见刊截图来源:…...

Could not find resource jdbc.properties问题的解决
以如下开头的内容: Exception in thread "main" org.apache.ibatis.exceptions.PersistenceException: ### Error building SqlSession. ### The error may exist in SQL Mapper Configuration 出现以上问题是没有在src/main/resources下创建jdbc.prop…...

【面试题】==与equals区别、Hashcode作用、hashcode相同equals()也一定为true吗?泛型特点与好处
文章目录1. 和 equals 的区别是什么?2.Hashcode的作用3. 两个对象的hashCode() 相同, 那么equals()也一定为 true吗?4.泛型常用特点5.使用泛型的好处?1. 和 equals 的区别是什么? “” 对于基本类型和引用类型 的作…...