2024年Java最新Java多线程基础知识总结,蚂蚁金服内推四面

最后

Java架构进阶面试及知识点文档笔记

这份文档共498页,其中包括Java集合,并发编程,JVM,Dubbo,Redis,Spring全家桶,MySQL,Kafka等面试解析及知识点整理

image

Java分布式高级面试问题解析文档

其中都是包括分布式的面试问题解析,内容有分布式消息队列,Redis缓存,分库分表,微服务架构,分布式高可用,读写分离等等!

image

互联网Java程序员面试必备问题解析及文档学习笔记

image

Java架构进阶视频解析合集

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

3、代码实例

以AtomicInteger的修改为例查看使用CAS时如何无锁并安全的修改某个值的:

public final int getAndUpdate(IntUnaryOperator updateFunction) {

int prev, next;

do {

prev = get();

next = updateFunction.applyAsInt(prev);

} while (!compareAndSet(prev, next));

return prev;

}

先获得要修改的原值prev和要改成的新值next,当使用CAS替换新值不成功时,自旋,重新获得原值和新值再试一次直到成功为止。

这段代码中可以发现两个问题:

  • CAS操作必须是原子性的,即操作中间无法被打断。

  • 获取原值时要保证这个原值对本线程可见。

CAS其实是调用了JNI,使用本地方法来保证原子性。

JNI(Java native interface),通过使用Java本地接口书写程序,可以确保代码在不同的平台上方便移植。

4、CAS带来的问题

(1)ABA问题

CAS操作的流程为:

  • 读取原值。

  • 通过原子操作比较和替换。

虽然比较和替换是原子性的,但是读取原值和比较替换这两步不是原子性的,期间原值可能被其它线程修改。

ABA问题有些时候对系统不会产生问题,但是有些时候却也是致命的。

ABA问题的解决方法是对该变量增加一个版本号,每次修改都会更新其版本号。JUC包中提供了一个类AtomicStampedReference,这个类中维护了一个版本号,每次对值的修改都会改动版本号。

(2)自旋次数过多

CAS操作在不成功时会重新读取内存值并自旋尝试,当系统的并发量非常高时即每次读取新值之后该值又被改动,导致CAS操作失败并不断的自旋重试,此时使用CAS并不能提高效率,反而会因为自旋次数过多还不如直接加锁进行操作的效率高。

(3)只能保证一个变量的原子性

当对一个变量操作时,CAS可以保证原子性,但同时操作多个变量时CAS就无能为力了。

可以封装成对象,再对对象进行CAS操作,或者直接加锁。

四、多线程锁的升级原理是什么?


锁的状态总共有四种,无锁状态、偏向锁、轻量级锁、重量级锁。

随着锁的竞争,锁可以从偏向锁升级到轻量级锁,再升级到重量级锁。但是锁的升级是单向的,只能升级不能降级。

1、无锁

没有对资源进行锁定,所有的线程都能访问并修改同一个资源,但同时只有一个线程能修改成功,其它修改失败的线程会不断重试直到修改成功。

无锁总是假设对共享资源的访问没有冲突,线程可以不停执行,无需加锁,无需等待,一旦发现冲突,无锁策略则采用一种称为CAS的技术来保证线程执行的安全性,CAS是无锁技术的关键。

2、偏向锁

对象的代码一直被同一线程执行,不存在多个线程竞争,该线程在后续执行中自动获取锁,降低获取锁带来的性能开销。偏向锁,指的是偏向第一个加锁线程,该线程是不会主动释放偏向锁的,只有当其他线程尝试竞争偏向锁才会被释放。

偏向锁的撤销,需要在某个时间点上没有字节码正在执行时,先暂停偏向锁的线程,然后判断锁对象是否处于被锁定状态,如果线程不处于活动状态,则将对象头设置成无锁状态,并撤销偏向锁。

如果线程处于活动状态,升级为轻量级锁的状态

3、轻量级锁

轻量级锁是指当锁是偏向锁的时候,被第二个线程B访问,此时偏向锁就会升级为轻量级锁,线程B会通过自旋的形式尝试获取锁,线程不会阻塞,从er提升性能。

当前只有一个等待线程,则该线程将通过自旋进行等待。但是当自旋超过一定次数时,轻量级锁边会升级为重量级锁,当一个线程已持有锁,另一个线程在自旋,而此时第三个线程来访时,轻量级锁也会升级为重量级锁。

注:自旋是什么?

自旋(spinlock)是指当一个线程获取锁的时候,如果锁已经被其它线程获取,那么该线程将循环等待,然后不断的判断锁是否能够被成功获取,直到获取到锁才会退出循环。

4、重量级锁

指当有一个线程获取锁之后,其余所有等待获取该锁的线程都会处于阻塞状态。

重量级锁通过对象内部的监听器(monitor)实现,而其中monitor的本质是依赖于底层操作系统的Mutex Lock实现,操作系统实现线程之间的切换需要从用户态切换到内核态,切换成本非常高。

5、锁状态对比

 偏向锁轻量级锁重量级锁
使用场景只有一个线程进入同步块虽然很多线程,但没有冲突,线程进入时间错开因而并未争抢锁发生了锁争抢的情况,多条线程进入同步块争用锁
本质取消同步操作CAS操作代替互斥同步互斥同步
优点不阻塞,执行效率高(只有第一次获取偏向锁时需要CAS操作,后面只是比对ThreadId)不会阻塞不会空耗CPU
缺点

适用场景太局限。若竞争产生,会有额外的偏向锁撤销的消耗

长时间获取不到锁空耗CPU阻塞,上下文切换,重量级操作,消耗操作系统资源

6、锁消除

消除锁是虚拟机另外一种锁的优化,这种优化更彻底,Java虚拟机在JIT编译时(可以简单理解为当某段代码即将第一次被执行时进行编译,又称即时编译),通过对运行上下文的扫描,去除不可能存在共享资源竞争的锁,通过这种方式消除没有必要的锁,可以节省毫无意义的请求锁时间,如StringBuffer的append是一个同步方法,但是在add方法中的StringBuffer属于一个局部变量,别切不会被其它线程所使用,因此StringBuffer不可能存在共享资源竞争的情景,JVM会自动将其锁消除。

五、Synchronized的特性


1、可重入性

synchronized的锁对象中有一个计数器(recursions变量)会记录线程获得几次锁;

可重入的好处:

(1)可以避免死锁;

(2)可以让我们更好的封装代码;

synchronized是可重入锁,每部锁对象会有一个计数器记录线程获取几次锁,在执行完同步代码块时,计数器的数量会-1,直到计数器的数量为0,就释放这个锁。

2、不可中断性

一个线程获得锁后,另一个线程想要获得锁,必须处于阻塞或等待状态,如果第一个线程不释放锁,第二个线程会一直阻塞或等待,不可被中断;

synchronized 属于不可被中断;

Lock lock方法是不可中断的;

Lock tryLock方法是可中断的;

六、Synchronized保证了原子性、可见性、有序性


1、Synchronized保证原子性

public class Test {

private static int number = 0;

private static Object obj = new Object();

public static void main(String[] args) throws InterruptedException {

Runnable increment = () -> {

for (int i = 0;i<1000;i++){

number++;

}

};

List list = new ArrayList<>();

for (int i = 0; i < 5; i++) {

Thread t = new Thread(increment);

t.start();

list.add(t);

}

for (Thread t : list) {

t.join();

}

System.out.println("number = " + number);

}

}

Synchronized保证原子性

Runnable increment = () -> {

for (int i = 0;i<1000;i++){

synchronized (obj){

number++;

}

}

};

2、Synchronized保证可见性

public class Test1 {

public static boolean flag = true;

public static void main(String[] args) throws InterruptedException {

new Thread(()->{

while (flag){

}

}).start();

Thread.sleep(2000);

new Thread(()->{

flag = false;

System.out.println(“线程修改了变量的值为false”);

}).start();

}

}

volatile即可解决这个问题!

public static volatile boolean flag = true;

Synchronized保证可见性

Synchronized保证可见性的原理,执行Synchronized时,会对应lock原子操作会刷新工作内存中共享变量的值。

3、Synchronized保证有序性

有序性:即程序执行的顺序按照代码的先后顺序执行。

在Java内存模型中,允许编译器和处理器对指令进行重排序,但是重排序过程不会影响到单线程程序的执行,却会影响到多线程并发执行的正确性。

在Java里面,可以通过volatile关键字来保证一定的“有序性”。另外可以通过synchronized和Lock来保证有序性,很显然,synchronized和Lock保证每个时刻只有一个线程执行同步代码,相当于是让线程顺序执行同步代码,自然就保证了有序性。

七、synchronized的三种应用方式


1、修饰普通方法

作用于当前方法加锁,进入同步代码前要获得当前实例的锁。

(1)synchronized保证线程安全

(2)synchronized什么情况下无法保证线程安全

(3)这时synchronized修饰在静态方法上,就可以解决这个问题了。

start()、run()、join()的区别:

start():线程不会立即启动。相当于是在就绪队列里面;

run():启动线程;

join():主要作用是同步,它可以使得线程之间的并行执行变为串行执行。

join方法的作用:

在A线程中调用了B线程的join方法,表示只有当B线程执行完毕后,A线程才能继续执行。注意调用的join方法是没有传参的,join方法其实可以传递一个参数给它,如果A线程中掉用B线程的join(10),则表示A线程会等待B线程执行10毫秒,10毫秒过后,A、B线程并行执行。需要注意的是,jdk规定,join(0)的意思不是A线程等待B线程0秒,而是A线程等待B线程无限时间,直到B线程执行完毕,即join(0)等价于join()。

2、修饰静态方法

作用于当前类对象加锁,进入同步代码前要获得当前类对象的锁。

但我们应该意识到这种情况下可能会发现线程安全问题(操作了共享静态变量i)。

3、修饰同步代码块

指定加锁对象,对给定对象加锁,进入同步代码前要获得给定对象的锁。

除了使用关键字修饰实例方法和静态方法外,还可以使用同步代码块,在某些情况下,我们编写的方法体可能比较大,同时存在一些比较耗时的操作,而需要同步的代码又只有一小部分,如果直接对整个方法进行同步操作,可能会得不偿失,此时我们可以使用同步代码块的方式对需要同步的代码进行包裹,这样就无需对整个方法进行同步操作了,同步代码块的使用示例如下:

从代码可以看出,将synchronized作用域一个给定的实例对象instance,即当前实例对象就是锁对象,每次当线程进入synchronized包裹的代码块时就会要求当前线程持有instance实例对象锁,如果当前有其它线程正持有该对象锁,那么新到的线程必须等待,这样也就保证了每次只有一个线程执行i++操作。当然除了使用instance作为对象外,还可以使用this对象(代表当前实例)或者当前类的class对象作为锁,如下:

//this,当前实例对象锁

synchronized(this){

for(int j=0;j<1000000;j++){

i++;

}

}

感受:

其实我投简历的时候,都不太敢投递阿里。因为在阿里一面前已经过了字节的三次面试,投阿里的简历一直没被捞,所以以为简历就挂了。

特别感谢一面的面试官捞了我,给了我机会,同时也认可我的努力和态度。对比我的面经和其他大佬的面经,自己真的是运气好。别人8成实力,我可能8成运气。所以对我而言,我要继续加倍努力,弥补自己技术上的不足,以及与科班大佬们基础上的差距。希望自己能继续保持学习的热情,继续努力走下去。

也祝愿各位同学,都能找到自己心动的offer。

分享我在这次面试前所做的准备(刷题复习资料以及一些大佬们的学习笔记和学习路线),都已经整理成了电子文档

拿到字节跳动offer后,简历被阿里捞了起来,二面迎来了P9"盘问"

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

his){

for(int j=0;j<1000000;j++){

i++;

}

}

感受:

其实我投简历的时候,都不太敢投递阿里。因为在阿里一面前已经过了字节的三次面试,投阿里的简历一直没被捞,所以以为简历就挂了。

特别感谢一面的面试官捞了我,给了我机会,同时也认可我的努力和态度。对比我的面经和其他大佬的面经,自己真的是运气好。别人8成实力,我可能8成运气。所以对我而言,我要继续加倍努力,弥补自己技术上的不足,以及与科班大佬们基础上的差距。希望自己能继续保持学习的热情,继续努力走下去。

也祝愿各位同学,都能找到自己心动的offer。

分享我在这次面试前所做的准备(刷题复习资料以及一些大佬们的学习笔记和学习路线),都已经整理成了电子文档

[外链图片转存中…(img-xb2PjTea-1714915459689)]

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

  • 9
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值