【面试】Java并发篇(一)

并发全景图

一、并发产生背景:为什么出现并发?

二、并发理论基础:并发有哪些问题?根本原因是什么?解决理论有哪些?

线程安全怎么做的?
不安全会导致哪些问题?

2.1 基本概念

1、进程和线程

进程是最小资源分配单位,线程是最小运行单位,线程们共享同一个进程的内存空间等资源。一个进程下面能有一个或多个线程,每个线程都有独立一套的寄存器和栈,这样可确保线程控制流相对独立。
1

补充:
1、协程是为了避免线程IO阻塞,能去做其他事情,因此把程序逻辑封装在叫协程的抽象里。
追问1:Java线程有几种状态?

2

追问2:线程等待时位于哪个区域,具体讲一下

……(暂时不太清楚)

2、可重入锁的可重入性是什么意思,哪些是可重入锁?

可重入性:可以正确重复使用。
可重入锁:synchronized 和 ReentrantLock。

(待消化确认)可重入锁:自己可以再次获取自己的内部锁。比如,一个线程获得了某个对象的锁,此时这个对象锁还没有释放,当其再次想要获取这个对象的锁时,还可以再获取的;如果不可锁重入的话,就会造成死锁;同一个线程每次获取锁,锁的计数器都自增1,所以要等到锁的计数器下降为0时,才能最终释放锁。

3、JMM

Java 内存模型(JMM) 作用于工作内存(本地内存)和主存之间数据同步过程,它规定了如何做数据同步以及什么时候做数据同步,如下图。
4

4、线程的局部变量

局部变量:方法内部的变量。

//生成斐波那契数列
public int[] fibonacci(int n){
    //存放结果的数组
    int[] result = new int[n];
    //数组的第1项和第2项为1
    result[0] = result[1] = 1;
    //计算第3项到第n项
    for(int i = 2; i < n; i++){
        result[i] = result[i-2] + result[i-1];
    }
    return result;  // 调用result生成斐波那契数列,result是安全的
}

不涉及到数据竞争,是线程安全的。

追问1:为什么安全?

局部变量就是存放在调用栈里的,栈帧在调用方法时创建,返回时销毁。
4
而每个线程有自己独立的调用栈,所以不存在并发问题。
5

追问2:如何跨越方法边界呢?

变量必须创建在堆里。

5、什么是线程安全,怎么保证多线程线程安全?(*4)

线程安全:不同线程访问相同资源而不会产生错误或不可预知结果。

保证方式:synchronized、Volatile、并发工具类、Reentrant Locks等

补充:

方式内容具体
并发集合java.util.concurrent包ConcurrentHashMap()
原子对象AtomicInteger、AtomicLong、AtomicXXX……
同步方法synchronized修饰方法/语句synchronized关键字
Volatile解决线程间可见性问题,确保JVM读取/写入主内存,而不是CPU缓存。Volatile修饰变量
Reentrant Locks改进的Lock实现ReentrantLock
读/写锁可以实现没有线程写,就可以有许多线程读取该资源,否则阻止其他线程取ReadWriteLock
无状态实现
不可变实现
线程本地变量字段本地化,线程间不会共享
同步集合Collections.synchronizedCollection()
外部锁定

……

2.2、关键字:wait、sleep、notify、notifyAll

1、sleep和wait的区别,sleep会不会释放锁

对象位置说明异常捕获CPU、锁资源
sleepThread线程类方法线程状态控制Y(必须)释放CPU资源,不释放锁资源
waitObject顶级类方法线程间通信N(不需)两者都释放
追问1:sleep会不会释放锁?

[外层包有synchronized] 不会,只是释放CPU资源,锁资源并没有释放。

2、notify和notifyAll的区别

notifyAll会让 所有 等待池 的线程进入 锁池 去竞争锁的机会;
notify只会 随机选取一个 等待池的线程进入锁池去竞争锁的的机会。

补充:

1、锁池
假设线程A已经拥有了某个对象ObjectA(不是类)的锁,而其他线程B,C想要调用这个对象的某个sychronized方法(或者块)。
线程必须先获得该对象锁的拥有权,才能进入对象的的synchronized方法。
但是ObjectA的锁正被线程A拥有,所以线程BC会被阻塞,进入一个地方【对象锁池】去等待锁释放。

简要理解,锁池就是 线程 获取锁进行等待的地方。

2、等待池
假设线程A调用了某个对象ObjectA的wait方法,线程A就会释放对象的锁;同时线程A就进入ObjectA的等待池,进入等待池中的线程不会去竞争ObjectA的锁

重点:锁池和等待池都是针对对象。
追问1:为什么notify()可能会导致死锁,而notifyAll()则不会?

notifyAll会将全部线程由等待池移到锁池参与锁竞争,成功则继续执行,如果不成功则留在锁池等待锁被释放后再次参与竞争;而notify只会唤醒一个线程,所以可能导致死锁。

四、参考

1、程序员必需清楚的进程和线程
2、Kotlin 协程实践之进程、线程、协程
3、进程、线程与协程还傻傻分不清?P7 大佬大白话讲解,直接秒懂
4、09 | Java线程(上):Java线程的生命周期
5、什么是线程安全?如何实现?
6、【高并发】面试官问我:为什么局部变量是线程安全的?

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值