趁着金三银四的季节,想着去大厂面试试一下,说不准咱遇到一个大冤种面试官成功上岸了呢。谁知道一上来面试官简历都没看,直接给我来了套并发多线程组合拳,直接把我给干懵了,大佬是我不配,打扰了。
回来后我把面试官的问题复盘了一下,现在就分享给大家,希望对大家面试有所帮助。当然,如果觉得博主总结的不错的话,希望能给个三连支持一下,让更多的人看到,谢谢啦。另外我总结了最新的面试题文档,包含spring、并发、数据库、Redis、分布式、dubbo、JVM、微服务等方面总结,大家可以看下方这篇文章。
面试必拿下!Java程序员面试95%被问到的面试题总结
1、什么是进程?什么是线程?(一个简单的暖场题)
进程是操作系统分配资源的最小单元
线程是操作系统调度的最小单元,线程隶属于进程。
一个程序至少有一个进程,一个进程至少有一个线程。
2、JAVA如何开启线程?怎么保证线程安全?
如何开启线程?
1、继承Thread类,重写run方法。
2、实现Runnable接口,实现run方法。
3、实现Callable接口,实现call方法。通过FutureTask创建一个线程,获取到线程执行的返回值。
4、通过线程池来开启线程。
怎么保证线程安全?
加锁: 1、 JVM提供的锁, 也就是Synchronized关键字。
2、 JDK提供的各种锁 Lock。
3、 Volatile和Synchronized有什么区别?Volatile能不能保证线程安全?DCL(DoubleCheck Lock)单例为什么要加Volatile?
1、Synchronized关键字,用来加锁。 Volatile只是保持变量的线程可见性。通常适用于一个线程写,多个线程读的场景。
2、不能。Volatile关键字只能保证线程可见性, 不能保证原子性。
3、Volatile防止指令重排。在DCL中,防止高并发情况下,指令重排造成的线程安全问题。
4、JAVA线程锁机制是怎样的?偏向锁、轻量级锁、重量级锁有什么区别?锁机制是如何升级的?
1、JAVA的锁就是在对象的Markword中记录一个锁状态。无锁,偏向锁,轻量级锁,重量级锁对应不同的锁状态。
2、JAVA的锁机制就是根据资源竞争的激烈程度不断进行锁升级的过程。
3、
5、谈谈你对AQS的理解。AQS如何实现可重入锁?
1、AQS是一个JAVA线程同步的框架。是JDK中很多锁工具的核心实现框架。
2、 在AQS中,维护了一个信号量state和一个线程组成的双向链表队列。其中,这个线程队列,就是用来给线程排队的,而state就像是一个红绿灯,用来控制线程排队或者放行的。 在不同的场景下,有不用的意义。
3、在可重入锁这个场景下,state就用来表示加锁的次数。0标识无锁,每加一次锁,state就加1。释放锁state就减1。
6、Java中的volatile 变量有什么作用?
1、确保多线程可见性
2、禁止指令重排序
7、Lock接口(Lock interface)是什么?对比synchronized它有什么优势?
Lock接口比同步方法和同步块提供了更具扩展性的锁操作。
Lock是synchronized的扩展版,Lock提供了无条件的、可轮询的(tryLock方法)、定时的(tryLock带参方法)、可中
断的(lockInterruptibly)、可多条件队列的(newCondition方法)锁操作。另外Lock的实现类基本都支持非公平锁(默
认)和公平锁,synchronized只支持非公平锁,当然,在大部分情况下,非公平锁是高效的选择。
8、乐观锁和悲观锁的理解及如何实现,有哪些实现方式?
悲观锁:总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样
别人想拿这个数据就会阻塞直到它拿到锁。Java里面的同步原语synchronized关键字的实现是悲观锁。
乐观锁:顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会
判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。在Java中原子变量类就是使用了乐观锁的
一种实现方式CAS实现
乐观锁的实现方式:
- 使用版本标识来确定读到的数据与提交时的数据是否一致。提交后修改版本标识,不一致时可以采取丢弃和再次
尝试的策略。 - java中的Compare and Swap即CAS ,当多个线程尝试使用CAS同时更新同一个变量时,只有其中一个线程能更
新变量的值,而其它线程都失败,失败的线程并不会被挂起,而是被告知这次竞争中失败,并可以再次尝试。
9、有三个线程T1,T2,T3,怎么确保它们按顺序执行?
1、先启动最后一个(T3调用T2,T2调用T1)
package com.tuling;
public class Testt {
static Testt t=new Testt();
class T1 extends Thread{
@Override
public void run() {
//T3线程中要处理的东西
System.out.println("T1线程执行");
}
}
class T2 extends Thread{
@Override
public void run() {
//T3线程中要处理的东西
System.out.println("T2线程执行");
t.new T1().start();
}
}
class T3 extends Thread{
@Override
public void run() {
//T3线程中要处理的东西
System.out.println("T3线程执行");
t.new T2().start();
}
}
public static void main(String[] args) {
t.new T3().start();
}
}
2.可以用线程类的join()方法在一个线程中启动另一个线程,另一个线程完成
10、你对线程优先级的理解是什么?
每一个线程都是有优先级的,一般来说,高优先级的线程在运行时会具有优先权,但这依赖于线程调度的实现,这
个实现是和操作系统相关的(OS dependent)。我 们可以定义线程的优先级,但是这并不能保证高优先级的线程会
在低优先级的线程前执行。线程优先级是一个 int 变量(从 1-10),1 代表最低优先级,10 代表最高优先级。
java 的线程优先级调度会委托给操作系统去处理,所以与具体的操作系统优先级有关,如非特别需要,一般无需设
置线程优先级。