欢迎分享,原创不易,请带上作者信息,谢谢
掘金栏目地址: juejin.cn/collection/…
今天和面试官朋友闲聊的时候,他说最近面了一位JAVA开发,他本着认真负责(炫技)的态度,问了一些JAVA多线程的问题竟然被面试者正面刚了一波!现在的面试者越来越厉害了,魔鬼的细节懂的越来越多,搞的他慌的一匹。
别慌,来我这里收集题目,明天继续和他正面刚!
我们当然是由浅入深,试试他的深浅,慢慢调情,不是,慢慢从入门开始。
线程、进程、程序?
什么是线程、什么是进程、什么是程序?
上来先给他醍醐灌顶,三连问让他瞬间懵逼,否则还以为大老虎是吃素的,嘿嘿嘿。
看着他微微泛红的脸颊,不知所措的眼神,我的目的就达到了,嘿嘿嘿。
沉默了十秒,我清了清嗓子。
年轻人,基础还不够扎实阿,你想想,程序不就是你写的代码?它不是静静的躺在你的硬盘上吗?进程不就是操作系统把你的代码跑起来了,然后再根据一些规则来调度这个进程吗?线程就更好理解了,你一个进程内部是不是可以启动多个线程干不同的事情?那么线程就是一段程序不同的执行路径。
总的来说
- 1.程序:一段静态的代码
- 2.进程:运行起来的程序
- 3.线程:进程的最小执行单位,一段程序不同的执行路径
看着面试者崇拜的眼神,我知道,又征服一位小菜鸟。
并发与并行
我们这边呢,经常有一些高并发的场景,简单的说一说你怎么理解并发与并行的吧。
一个简单而又深刻的问题,关键是在不经意间突出了我们大流量高并发的架构和场景,我们可不是low X 的CURD,我简直是天才面试官。
- 并发:同一时刻发出请求,可以同时执行,也可能不同时执行。主要指的是任务提交这个动作。
- 并行:任务真正的同时执行,在多核CPU的情况,多个CPU同时工作。主要指的是任务执行的过程。
并发concurrent,比如我们可以对Redis进行并发请求,但是因为他的单线程模型,所以他不是并行执行(先别扯I/O Thread)。 并行的话parallel,JAVA的垃圾回收器中就有这种类型的存在。
JAVA中创建线程的方式
不错,可以说清楚还能举一反三,那你知道JAVA中创建线程的方式有几种?
面试者:这个我知道,两种,实现Runnable接口,继承Thread类。
嗯,不错,还能想出其他的答案吗?
额、、沉默了十秒,很明显他慌了,我又得逞了,底层基本就这两种,但是为了显示我高超的水平,怎么也要给他再整两个答案,嘿嘿嘿。
其实你说的不错,底层就基本这两种,你有没有了解过线程池也可以创建线程,Callable和Lambda表达式也可以创建线程?
还好他是小菜鸟,不然我要被怼死了,我真是一个机灵的小杠精。
- 1.实现Runnable接口
- 2.继承Thread类
- 3.其他的可以不说,实在要刚,线程池,Callable和Lambda表达
JAVA线程的状态
那好吧,我们来点简单的(其实不是那么简单,嘿嘿),JAVA的线程状态有哪些?另外列举几个改变线程状态的方法。
最后一个小问肯定打他一个措手不及,估计他都没听明白什么意思。
面试者:JAVA线程的状态有,初始、就绪、运行、阻塞、结束,改变线程的状态是指sleep?wait?
嗯,你这个答案我觉得可以,不过答的还不够完美,阻塞态还可以进一步细分,我画一张图你就明白了。
锁与CPU资源
synchronized你平时应该经常用吧?上面给你列了这么多的方法,你知道哪些方法会释放锁和CPU资源,哪些不会吗?
虽然这个问题和synchronized联系不是那么大,我直接给他定性他平常经常用synchronized,让他措手不及,毫无反驳之力,问题中深埋的陷阱让他知道社会的险恶。
-
sleep 不释放锁、释放cpu,时间到期回到Ready状态,等待cpu调度
-
join 释放锁、抢占cpu,子线程运行完,父线程才继续
-
yield 不释放锁、释放cpu,直接回到Ready状态,等待cpu调度
-
wait 释放锁、释放cpu
-
notify、notifyAll不释放锁、不释放cpu
-
Locksupport 和synchronize锁没什么关系
-
park 阻塞当前线程,释放CPU
-
unpark 解除阻塞不释放CPU
不错,回答比较全面,join真的会释放锁吗?你来看看我这个程序。
public class JoinReleaseTest {
public static void main(String[] args) throws InterruptedException {
final Object lock = new Object();
Thread t1 = new Thread(() -> {
synchronized (lock) {
System.out.println("t1能打印,说明释放锁了 ========");
}
});
synchronized (lock) {
t1.start();
t1.join();
System.out.println("main 执行完毕========");
}
}
}
//运行结果:无限等待
这是为啥?不是是放锁吗?为什么程序执行不下去了??
看着他慌乱的小眼神,我一定在他心里留下了不可磨灭的印象,哈哈哈哈。
沉默数十秒,是时候出手拯救他一下了。
注意这个问题我只讲一遍, 你看看,join的底层实现。
public final synchronized void join(long millis)
throws InterruptedException {
long base = System.currentTimeMillis();
long now = 0;
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (millis == 0) {
while (isAlive()) {
wait(0);
}
} else {
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
是不是用了一个循环,判断当前线程是否存活(isAlive()=this.isAlive()),存活的情况下就 wait。
前面你也说了wait是释放锁和CPU资源的,join()方法是在子线程调用的,wait是Object的方法,直接调用相当于子线程线程调用了this.wait();
所以释放的子线程这个对象的锁,而不是你定义的lock对象,明白了吗?
所以改成这样就可以执行了。
public class JoinReleaseTest {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (this){
System.out.println("t1能打印,说明释放锁了========");
}
}
},"子线程");
synchronized (t1) {
t1.start();
t1.join();
System.out.println("main 执行完毕========");
}
}
}
对了,你刚刚说你经常用synchronized,等我喝口水,我们继续聊聊synchronized。
- 原创不易,英雄点个赞再走。
- 可以关注微信公众号:面试官收题
- 领取一些我收集的JAVA学习和面试资料
- 有兴趣讨论问题也可以加我微信:gk6688225