JUC-并发面试题

一、基础

1、为什么要并发编程

  • 充分利用多核CPU

2、并发编程存在的问题

  • 上下文切换:CPU通过时间片分配来执行任务,当前任务执行一个时间片后会切换到下一个任务。在切换前会保存上一个任务的状态,以便下次切换回这个任务时,可以再次加载这个任务的状态。任务从保存到再加载的过程就是一次上下文切换。
    • 线程创建和上下文切换会带来额外的开销
  • 线程安全问题
  • 死锁:线程A持有资源,线程B持有资源;都想申请对方的资源,就会相互等待而进入死锁。(互相等待对方释放锁)
  • 资源限制

3、线程安全/并发编程三要素/如何保证多线程的运行安全

  • 原子性:一个或多个操作要么同时成功要么同时失败(原子类、synchronized、lock)
  • 可见性:一个线程对共享变量修改,另一个线程能够立即看到(volatile、synchronized、Lock)
  • 有序性:程序执行顺序按照代码的先后顺序执行(volatile、Happens-before)(处理器可能指令重排)

4、并发和并行的区别

  • 并发:多个线程操作同一资源,CPU快速切换
  • 并行:多个处理器同时处理多个任务

5、同步方法和同步块,哪个是更好的选择

  • 同步块是更好的选择,因为它不会锁住整个对象。同步方法会锁住整个对象
  • 同步块更要符合开放调用的原则,只在需要锁住的代码块锁住相应的对象
  • 同步的范围越小越好

6、synchronized 底层实现原理

  • 同步方法块使用的是monitorenter和monitorexit,monitorenter指令指向同步代码块开始的位置,monitorexit指向同步代码块结束的位置;线程执行到monitorenter指令时,将会尝试获取对象所对应的monitor所有权,即尝试获得对象的锁
  • 同步方法:使用ACC_SYNCHRONIZED标识,指明该方法是一个同步方法
  • 本质是对一个对象的监视器( monitor )进行获取,而这个获取过程是排他的,也就是同一 时刻只能有一个线程获取到由 synchronized 所保护对象的监视器
  • monitor对象存在每个java对象的对象头中

7、synchronized可重入原理

  • 重入锁是指一个线程获取到该锁之后,该线程可以继续获得该锁。底层原理维护一个计数器,当线程获取该锁时,计数器加一,再次获得该锁时继续加一,释放锁时,计数器减一,当计数器值为0时,表明该锁未被任何线程所持有,其它线程可以竞争获取锁。

8、synchronized锁升级

  • Synchronized 在jdk1.6 版本之前,是通过重量级锁的方式来实现线程之间锁的竞争。底层依赖操作系统的Mutex Lock 来实现互斥功能。涉及到用户态向内核态的切换,这个切换会带来性能的损耗。
  • 锁的升级的目的:是为了减低锁带来的性能消耗
    • 偏向锁,就是直接把当前锁偏向于某个线程,简单来说就是通过CAS 修改偏向锁标记,这种锁适合同一个线程多次去申请同一个锁资源并且没有其他线程竞争的场景。
    • 轻量级锁也可以称为自旋锁,基于自适应自旋的机制,通过多次自旋重试去竞争锁。自旋锁优点在于它避免避免了用户态到内核态的切换带来的性能开销。
  • synchronized 会尝试使用偏向锁的方式去竞争锁资源,如果能够竞争到偏向锁,表示加锁成功直接返回。如果竞争锁失败,说明当前锁已经偏向了其他线程。需要将锁升级到轻量级锁,在轻量级锁状态下,竞争锁的线程根据自适应自旋次数去尝试抢占锁资源,如果在轻量级锁状态下还是没有竞争到锁,就只能升级到重量级锁,在重量级锁状态下,没有竞争到锁的线程就会被阻塞,线程状态是Blocked。

9、synchronized锁优化

锁存在四种状态:无锁状态、偏向锁状态、轻量级锁状态、重量级锁状态,随竞争而升级,不可降级。

  • 偏向锁:据统计,大部分锁80%的时间只有一个线程获取。当一个线程首次获得锁时,它会在对象头中记录下这个线程的ID,这样下次该线程再尝试获取锁时就可以无阻塞地直接获取。
    • 锁撤销:需要等待全局安全点
  • 轻量级锁:自旋获取锁
    • 自旋锁:如果一个线程尝试获取该锁,它会一直循环检测锁是否被释放,而不是进入线程挂起或睡眠状态。默认十次
    • 适应性自旋锁:一种特殊的自旋锁,其自旋的时间不再固定,而是根据前一次在同一个锁上的自旋时间及锁的拥有者的状态来决定。
  • 锁消除:虚拟机在编译的时候检测到共享数据不可能存在竞争,执行锁消除
  • 锁粗化:频繁的加锁和解锁会造成性能损失,把锁同步的范围扩大。

10、volatile的作用及实现原理

  • 可见性:当一个线程修改了volatile修饰的变量,会将这个值刷新到主内存中,并且会通知其他线程该变量发生变化。其他线程需要读取这个变量时,则会去主内存中读取最新的值。volatile的底层原理主要通过 lock 前缀指令实现的,它会锁定当前内存区域的缓存(缓存行),并且立即将当前缓存行数据写入主内存(耗时非常短),回写主内存的时候会通过 MESI 协议使其他线程缓存了该变量的地址失效,从而导致其他线程需要重新去主内存中重新读取数据到其工作线程中。
    • MESI 协议,是一种高速缓存一致性协议。它是为了解决多处理器(CPU)在并发环境下,多个 CPU 缓存不一致问题而提出的。主要用途是确保在多个 CPU 共享内存时,各个 CPU 的缓存数据能够保持一致性
  • 有序性:通过增加内存屏障防止多个指令之间的重排序
    • 内存屏障(Memory Barrier 或 Memory Fence)是一种硬件级别的同步操作,它强制处理器按照特定顺序执行内存访问操作,确保内存操作的顺序性,阻止编译器和 CPU 对内存操作进行不必要的重排序。内存屏障可以确保跨越屏障的读写操作不会交叉进行,以此维持程序的内存一致性模型。

11、synchronized 和 volatile 的区别是什么?

  • synchronized 表示只有一个线程可以获取作用对象的锁,阻塞其他线程。
  • volatile 表示变量在 CPU 的寄存器中是不确定的,必须从主存中读取。保证多线程环境下变量的可见性;禁止指令重排序。
  • volatile 是变量修饰符;synchronized 可以修饰类、方法、变量、代码块。
  • volatile 仅能实现变量的可见性,不能保证原子性;而 synchronized 则可以保证变量的修改可见性和原子性。
  • volatile 不会造成线程的阻塞;synchronized 可能会造成线程的阻塞。
  • volatile标记的变量不会被编译器优化;synchronized标记的变量可以被编译器优化。
  • volatile关键字是线程同步的轻量级实现,所以volatile性能肯定比synchronized关键字要好。但是volatile关键字只能用于变量而synchronized关键字可以修饰方法以及代码块。synchronized关键字在JDK1.6之后进行了主要包括为了减少获得锁和释放锁带来的性能消耗而引入的偏向锁和轻量级锁以及其它各种优化之后执行效率有了显著提升,实际开发中使用synchronized 关键字的场景还是更多一些。

12、DCL

public class Singleton {
    private volatile static Singleton uniqueInstance;
    private Singleton(){}
    public static Singleton getUniqueInstance(){
        //判断对象是否实例过
        if (uniqueInstance == null) {
            //类对象加锁
            synchronized (Singleton.class) {
                if (uniqueInstance == null) {
                    uniqueInstance = new Singleton();
                }
            }
        }
        return uniqueInstance;
    }
}

13、伪共享

  • 背景:
    • 为了提高CPU 的利用率,平衡CPU 和内存之间的速度差异,在CPU 里面设计了三级缓存。CPU 在向内存发起IO操作的时候,一次性会读取64个字节的数据作为一个缓存行,缓存到CPU 的高速缓存里面。在Java 中一个long 类型是8个字节,意味着一个缓存行可以存储8个long 类型的变量。缓存行的设计对于CPU 来说,可以有效的减少和内存的交互次数,从而避免了CPU的IO等待,以提升CPU的利用率。
    • 是什么:多个线程修改同一个缓存行里面的多个独立变量的时候,基于缓存一致性协议,就会无意中影响了彼此的性能
    • 解决方案:对齐填充

二、线程

1、线程和进程的区别

  • 进程:操作系统分配资源的基本单位
  • 线程:执行具体的任务和功能,是CPU调度和分配的最小单位
  • 内存分配:同一进程中线程共享本进程的地址空间和资源,进程之间地址空间和资源相互独立

2、java中创建线程的方式(单继承、多实现)

  • 继承Thread类,重写run()
  • 实现Runnable接口,重写run(),无返回值
  • 29
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值