3.Java并发常见面试题总结

什么是线程和进程?

进程是系统资源调度的基本单位

线程是cpu资源调度分配的最小单位

一个进程有多个线程。

什么是用户线程、内核线程?区别是什么

说说线程的生命周期和状态?

NEW

RUNNABLE

BLOCKED

WAITING

TIME_WAITING

TERMINATED

什么是线程上下文切换?

什么是线程死锁?

循环等待、互斥、不剥夺、请求与保持

sleep() 方法和 wait() 方法对比?

让出资源

主动被动

(重要)可以直接调用 Thread 类的 run 方法吗?Start和run的区别?

同步、异步

JMM(Java 内存模型)详解

什么是JMM?

  • java内存模型
  • 规定了线程和内存之间的关系
  • 约定了一些保证线程安全的规范,比如happen before原则

java线程与主内存的关系

  • 线程内部有共享变量副本
  • 当线程需要通信时,使用共有空间,通过复制的形式共享

什么是指令重排序?特点是什么?

比如一个new运算/

  • 申请内存
  • 初始化值
  • 指向引用对象

只要保证语义一致即可。

happens-before 原则是什么?干嘛用的?

A HB B

就说明语义上A一定发生在B之前。

JMM规定了一些HB规则:

volatile的写 HB 读

解锁 HB 加锁

A HB B HB C -- A HB C

并发编程三大特性:(重要)

原子性、可见性、有序性

volatile

两个作用:

保证可见性、保证有序性

可见性,通过提前把内容写入主内存,且要求线程访问时都通过主内存访问,保证了可见性。

有序性,通过内存屏障,把loadload、loadstore等屏障添加在读和写的之前和之后,就可以保证volatile的写一定在读之前,保证了指令重排序

缺点:没有保证原子性

这一点可以用+锁实现

乐观锁和悲观锁

乐观锁:

假想每一次。。。

CAS,重试,不阻塞

缺点,写多时会性能不足

悲观锁:

假想每一次。。。

互斥,阻塞

死锁问题。

乐观锁的实现、问题、如何解决

synchronized

如何使用 synchronized?(重点)

  • 加载实例方法
  • 加载静态方法
  • 加在对象上

构造方法可以用 synchronized 修饰么?

synchronized 底层原理了解吗?

在被锁之前和之后添加monitor enter、exit监视器。

ACC_SYNCHRONIZED

对象的内存布局?

  • 对象头
    • markword
      • lock锁状态
      • biased_lock:偏向锁标志位
      • 轻量级锁标志位,指向线程id
      • 重量级锁标志位,指向monitor的指针
    • 类指针
  • 实例数据
  • 补齐填充

如何查看对象的内存布局及锁状态?

JOL:java object layout

锁优化

1、自旋优化

2、批量重偏向、批量撤销

3、锁粗化

(重点)synchronized 和 volatile 有什么区别?

二者经常一起配合使用,比如单例模式

锁升级

  • 无锁00
  • 偏向锁(mark word对象头中保存线程的id)01
  • 轻量级锁10
  • 重锁11

ReentrantLock是什么?4个特点?

也是一种锁机制,但是比synchronized更灵活。

共同点:

  • 排他锁
  • 可重入锁

不同点:

  • 可充式
  • 非公平和公平
  • 可以指定唤醒
  • 可中断

synchronized+wait+notify

reentrantLock+await+signal(condition)

什么是可重入锁?

比如某一个方法上加了锁,但是这个方法是递归的,那么当同一个线程再次获取同一个对象的时候,可以获取到,就叫做可重入锁。

ReentrantLock加锁的执行流程?

就是AQS的加锁过程

  • state,当前资源是否可用,如果不可用,还要判断当前资源是否为当前线程,如果是则可重入
  • 如果可用,则判断等待队列是否为空,如果为空,则当前线程CAS抢占。否则加入阻塞队列CLH

ReentrantLock释放锁的执行流程

判断占用的是否为当前线程,如果是则state-1

再判断state是否为0.

唤醒等待队列

lock和tryLock的区别?(重要)

lock会阻塞

tryLock不会阻塞,直接返回结果

ReentrantReadWriteLock

前16位是读锁、后16位是写锁

AQS原理。

# 说说CopyOnWriteArrayList

线程安全的List。

原理是:写时复制

缺点:一致性得不到保证

# 说说fail-fast与fail-safe

遍历过程中不可以被修改

遍历过程中可用修改

Automic原子类

基本类型源自类AutomicInteger

数组类型原子类AtomicIntegerArray

引用类型原子类AtomicReference

对象字段更新器AtomicIntegerFieldupdateer

优势

如果不用原子类,计算时需要volatile+锁,保证原子性,但是如果直接使用原子类,就可以更简单的实现原子性。

AQS

介绍AQS?

抽象队列同步器。

有两个重要组成部分:

共享资源状态量state

阻塞队列双向链表CLH

独占锁?

ReentrantLock、ReentrantReadLock

共享资源状态量state为0或1

共享锁?

1、semaphore

  1. 功能:允许一个资源有K个线程同时访问它
  2. 原理:state初始为K,每获取acquire一次-1. 释放release一次+1.当state==0时表示满了,之后的阻塞队列。

2、CountDownLatch

  1. 功能:保证多个线程阻塞到同一个地方,然后一起开始,比如ABCD四个任务同时全部执行完再执行E任务。
  2. 原理:state初始为K,每latch一次-1,主线程await阻塞等待,当state==0时,主线程被唤醒。
  3. 特点:一次性的,用完就没了

如何自己使用AQS

  1. 继承AQS
  2. 重写tryAcquire
    1. CASstate
    2. SetOwnerThread
  3. 重写tryRelease
    1. getState
    2. setState
    3. setOwnerThread

ThreadLocal

作用?

存储结构?

  • ThreadLocalMap:
  • 每个线程都有一个它对应的ThreadLocalMap放在线程本地内存。
  • 每个ThreadLocalMap中的键值对,key表示ThreadLocal、Val表示存入的实际值。

内存泄漏问题?

4种引用类型

虚:用队列接收对象死亡的通知。(实际开发几乎用不到)

Threadlocal的内存泄漏

1、原因

2、为什么不都是弱引用?

3、为什么不都是强引用?

ThreadLocalMap .set/get详解

1、hash算法计算key

2、解决冲突

  1. 如果key为空且val为空,则存入
  2. 如果key不空,则下一个找
  3. 如果key空val不空,则说明此槽位失效,发生探测式清理,然后更新当前槽位

清理分类

探测式清理

启发式清理

扩容机制

  • 如果执行完启发式清理工作后,未清理到任何数据,且当前散列数组中Entry的数量已经达到了列表的扩容阈值(len*2/3),就开始执行rehash()逻辑
  • 先从头到尾清理key=null的槽位(使用expungeStaleEntry的探测式清理方法)、清理完之后再通过判断size >= threshold - threshold / 4 也就是size >= threshold * 3/4 来决定是否扩容。
  • 扩容后的tab的大小为oldLen * 2。遍历老的散列表,重新计算hash位置,然后放到新的tab数组中。

线程池

为什么要用线程池?

  1. 避免线程资源的浪费,线程复用
  2. 减少线程创建过程,加快响应速度
  3. 方便管理

如何创建线程池?

Executor

4种内置线程池

Fixed

Single

Cached(可无限扩容的线程池)

Schedule(定时任务的线程池)

ThreadPoolExecutor

参数:核心线程数、最大线程数、阻塞队列、

饱和处理、KeepAliveTime、UNIT、Factory

执行流程
  1. 先看核心线程满没满,美满就使用核心线程
  2. 再看阻塞队列满没满,没满就加入阻塞队列
  3. 最后看最大线程数满没满,没满就新建不多于最大线程数的一个线程;满了就执行包和策略
饱和策略有哪些?
  1. 默认抛出异常,并抛弃饱和的任务(AbortPolicy)
  2. 直接丢弃饱和任务,什么也不做
  3. 使用主线程执行饱和任务
  4. 用最后一个等待的任务替换饱和任务(CallerRunPolicy)
常用阻塞队列?
  1. LinkedBlockingQueue:Fixed、Single,Cache
  2. ArrayBlockingQueue:不可扩容,不容易OOM,性能较差
  3. DelayedWorkQueue:Schedule
  4. Priority:自定义优先级的线程池

线程数量过大过小都有哪些问题?如何设定线程池的大小?(重要)

线程池数量过大:频繁的上下文切换

过小:CPU资源没有充分利用,且容易导致阻塞队列爆了

N+1:其中1表示防止线程偶发的缺页中断

2 * N或者 N + N * 线程等待时间 / 线程运行时间

线程池的五种状态

RUNNING

SHUTDOWN

STOP

TIDYING

TERMINATED

线程的5种状态

STARTED

RUNNING

WAITING

TIME_WAITING

TERMINATED

BLOCKING

合理关闭线程池?

ShutDown + AwaitTermination

Runnable vs Callable

execute() vs submit()

shutdown()VSshutdownNow()

isShutdown() VS isTerminated()

Future、Callable、FutureTask

Yield、Sleep、Wait、Notify对锁的影响?

# 如何优雅的中断线程

interrupt()

interrupted()

InterruptedExeception

线程的创建有哪几种?

  1. 继承Thread、、、
  2. 实现Callable、、、
  3. 线程池

Run()和Start()/execute()的区别

CPU核心数和线程数的关系

说说进程间的通信

  1. 管道 |
  2. 命名管道 >
  3. 消息队列
  4. 共享内存
  5. socket
相关推荐
码农派大星。几秒前
Spring Boot 配置文件
java·spring boot·后端
顾北川_野7 分钟前
Android 手机设备的OEM-unlock解锁 和 adb push文件
android·java
江深竹静,一苇以航10 分钟前
springboot3项目整合Mybatis-plus启动项目报错:Invalid bean definition with name ‘xxxMapper‘
java·spring boot
远望清一色16 分钟前
基于MATLAB的实现垃圾分类Matlab源码
开发语言·matlab
confiself25 分钟前
大模型系列——LLAMA-O1 复刻代码解读
java·开发语言
Wlq041530 分钟前
J2EE平台
java·java-ee
XiaoLeisj37 分钟前
【JavaEE初阶 — 多线程】Thread类的方法&线程生命周期
java·开发语言·java-ee
杜杜的man40 分钟前
【go从零单排】go中的结构体struct和method
开发语言·后端·golang
幼儿园老大*41 分钟前
走进 Go 语言基础语法
开发语言·后端·学习·golang·go
半桶水专家42 分钟前
go语言中package详解
开发语言·golang·xcode