前言
上一期小编发布了一份全网最全的八股文面试题,同学领取到后都说好,但就是太多了三个月都不一定背完,能不能出一些专题呀,于是小编应各位同学的要求,特推出这一期的JVM与高并发专题,由于文章限制本文摘取部分,需要的同学可以直接划到文末领取完整版PDF文档资料。废话不多说,正文走起!
1、什么是进程?什么是线程?
进程是操作系统分配资源的最小单元线程是操作系统调度的最小单元。
一个程序至少有一个进程,一个进程至少有一个线程。
2、JVM、JRE、JDK的关系?
JVM:(Java Virtual Machine),Java 虚拟机。它能识别 .class后缀的文件,并且能够解析它的指令,最终调用操作系统上的函数,完成我们想要的操作。
JRE:(Java Runtime Environment)Java 的运行时环境,JVM 标准加上实现的一大堆基础类库,就组成了 Java
的运行时环境
JDK:(Java Development Kit),除了包含 JRE,JDK 还提供了一些非常好用的工具,比如 javac、java、jar 等。
3、JVM中可以运行多种语言吗?
JVM只识别字节码,所以JVM其实跟语言是解耦的,也就是没有直接关联。 像Scala、Groovy 、Kotlin等等语言都可以在JVM上运行
4、JVM有哪些内存区域?
虚拟机栈:在JVM运行过程中存储当前线程运行方法所需的数据,指令、返回地址。
本地方法栈:本地方法栈是和虚拟机栈非常相似的一个区域,它服务的对象是 native 方法。程序计数器:主要用来记录各个线程执行的字节码的地址。
方法区:JDK1.7及之前“永久代”,JDK1.8及以后“元空间”,存放类的信息、常量池、方法数据、方法代码
堆:堆是 JVM 上最大的内存区域,我们申请的几乎所有的对象,都是在堆中存储。
5、堆空间大小怎么配置?各区域怎么划?
例子:活跃数据300M: 总堆:300M*4=1.2g
新生代=450M
老年代=750M
永久代/元空间=300M
6、JVM中哪些内存区域会发生内存溢出(OOM)?
栈溢出堆溢出
方法区溢出
本机直接内存溢出
7、JVM在创建对象时采用了哪些并发安全机制?
CAS+失败重试
本地线程分配缓冲(Thread Local Allocation Buffer,TLAB)
每个线程在Java堆中预先分配一小块私有内存,也就是本地线程分配缓冲,这样每个线程都单独拥有一个Buffer, 如果需要分配内存,就在自己的Buffer上分配,这样就不存在竞争的情况,可以大大提升分配效率
8、什么是对象头?对象头里面有哪些东西?
9、什么是复制算法?它有什么优缺点?
10、什么是标记整理算法?它有什么优缺点?
11、扩容新生代为什么能提高GC的效率?
T1+T2
新生代空间 200m, 每隔5s GC一次,每次GC耗时100ms T1+T2=100ms
A对象 存活7s
新生代空间 400m, 每隔10s GC一次 每次GC耗时200ms 2*T1
A对象 存活7s
12、阐述下CMS垃圾回收器,它有哪些问题?
Concurrent Mark Sweep
步骤
初始标记并发标记重新标记并发清除
CMS中的问题:
CPU敏感浮动垃圾
内存碎片 —垃圾回收器 退化 SerialOld 单线程的垃圾回收(标记整理算法)
13、在java中守护线程和用户线程的区别?
java中的线程分为两种:守护线程(Daemon)和用户线程(User)。守护线程通过调用Thread.setDaemon(true)设置。
一般程序使用的都是用户线程。
守护线程我们一般用不上,比如垃圾回收线程就是守护线程(Daemon)。 使用守护线程注意点
- Thread.setDaemon()必须在Thread.start()之前调用,否则运行时会抛出异常。
- 守护线程(Daemon)是为其他线程提供服务,如果全部的User Thread已经结束,守护线程没有可服务的线程,JVM关闭。
14、什么是多线程中的上下文切换?
CPU时间片 :CPU 时间片是 CPU 分配给每个线程执行的时间段,一般为几十毫秒。
上下文切换:当一个线程的时间片用完了,或者因自身原因被迫暂停运行了,这个时候,另外一个线程(可以是同 一个线程或者其它进程的线程)就会被操作系统选中,来占用处理器。这种一个线程被暂停剥夺使用权,另外一个 线程被选中开始或者继续运行的过程就叫做上下文切换。
上下文:在这种切出切入的过程中,操作系统需要保存和恢复相应的进度信息,这个进度信息就是“上下文”了
15、什么是死锁?死锁的危害?
死锁:是指两个或两个以上的进程(或线程)在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力 作用,它们都将无法推进下去。
危害:
1、死锁会使进程得不到正确的结果
2、死锁会使资源的利用率降低
3、死锁还会导致产生新的死锁。
16、在Java中Executor和Executors的区别?
17、什么是CAS操作,缺点是什么?
CAS中的问题
1、开销大
2、只能确保一个共享变量
3、ABA问题
18、Lock接口(Lock interface)是什么?对比synchronized它有什么优势?
Lock接口比同步方法和同步块提供了更具扩展性的锁操作。
Lock是synchronized的扩展版,Lock提供了无条件的、可轮询的(tryLock方法)、定时的(tryLock带参方法)、可中 断的(lockInterruptibly)、可多条件队列的(newCondition方法)锁操作。另外Lock的实现类基本都支持非公平锁(默认)和公平锁,synchronized只支持非公平锁,当然,在大部分情况下,非公平锁是高效的选择。
19、什么是阻塞队列?阻塞队列的实现原理是什么?
阻塞队列(BlockingQueue)是一个支持两个附加操作的队列。
这两个附加的操作是:在队列为空时,获取元素的线程会等待队列变为非空。当队列满时,存储元素的线程会等待 队列可用。
阻塞队列在实现上,主要是利用了Condition和Lock的等待通知模式。
20、什么是Callable和Future?
Callable接口类似于Runnable,从名字就可以看出来了,但是Runnable不会返回结果,并且无法抛出返回结果的异常
而Callable功能更强大一些,被线程执行后,可以返回值,这个返回值可以被Future拿到,也就是说,Future可以拿到异步执行任务的返回值。
Callable可以认为是带有回调的Runnable。
Future接口表示异步任务,是还没有完成的任务给出的未来结果。所以说Callable用于产生结果,Future用于获取结果。
21、什么是FutureTask?
FutureTask表示一个可以取消的异步运算。它有启动和取消运算、查询运算是否完成和取回运算结果等方法。只有当运算完成的时候结果才能取回,如果运算尚未完成get方法将会阻塞。
一个FutureTask对象可以对调用了Callable和Runnable的对象进行包装,由于FutureTask也是调用了Runnable接口所以它可以提交给Executor来执行。
22、什么是并发容器的实现?
并发容器可以简单地理解为通过synchronized来实现同步的容器,如果有多个线程调用同步容器的方法,它们将会串行执行。比如Vector,Hashtable,以及Collections.synchronizedSet,synchronizedList等方法返回的容器。
23、为什么我们调用start()方法时会执行run()方法,为什么我们不能直接调用run()方法?
当你调用start()方法时你将创建新的线程,并且执行在run()方法里的代码。
但是如果你直接调用run()方法,它不会创建新的线程也不会执行调用线程的代码,只会把run方法当作普通方法去 执行。
24、什么是不可变对象,它对写并发应用有什么帮助?
不可变对象(Immutable Objects)即对象一旦被创建它的状态(对象的数据,也即对象属性值)就不能改变,反之即为可变对象(Mutable Objects)。
Java平台类库中包含许多不可变类,如String、基本类型的包装类、BigInteger和BigDecimal等。
不可变对象天生是线程安全的。它们的常量(域)是在构造函数中创建的。既然它们的状态无法修改,这些常量永 远不会变。
25、乐观锁和悲观锁的理解及如何实现,有哪些实现方式?
悲观锁:总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样 别人想拿这个数据就会阻塞直到它拿到锁。Java里面的同步原语synchronized关键字的实现是悲观锁。
乐观锁:顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会 判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。在Java中原子变量类就是使用了乐观锁的一种实现方式CAS实现
乐观锁的实现方式:
- 使用版本标识来确定读到的数据与提交时的数据是否一致。提交后修改版本标识,不一致时可以采取丢弃和再次 尝试的策略。
- java中的Compare and Swap即CAS ,当多个线程尝试使用CAS同时更新同一个变量时,只有其中一个线程能更新变量的值,而其它线程都失败,失败的线程并不会被挂起,而是被告知这次竞争中失败,并可以再次尝试。
26、在java中wait和sleep方法的不同?
最大的不同是在等待时wait会释放锁,而sleep一直持有锁。Wait通常被用于线程间交互,sleep通常被用于暂停执行。
27、为什么wait, notify 和 notifyAll这些方法不在thread类里面?
JAVA提供的锁是对象级的而不是线程级的锁。
每个对象都有锁,通过线程获得。如果线程需要等待某些锁那么调用对象中的wait()方法就有意义了。如果wait()方法定义在Thread类中,线程正在等待的是哪个锁就不明显了。
简单的说,由于wait,notify和notifyAll都是锁级别的操作,所以把他们定义在Object类中因为锁属于对象。
28、 Java内存模型是什么?
计算机多级缓存架构
Java内存模型( JMM)
可见性问题安全性问题重排序问题
29、什么是线程安全?
当多个线程访问某个类时,不管运行时环境采用何种调度方式或者这些线程将如何交替执行,并且在调用代码中不 需要任何额外的同步或者协同,这个类都能表现出正确的行为,那么就称这个类是线程安全的。
不可变
像String、Integer、Long这些,都是final类型的类,任何一个线程都改变不了它们的值,要改变除非新创建一个,因此这些不可变对象不需要任何同步手段就可以直接在多线程环境下使用
加锁和CAS
我们最常使用的保证线程安全的手段,使用synchronized关键字,使用显式锁,使用各种原子变量,修改数据时 使用CAS机制等等。
30、一个线程如果出现了运行时异常会怎么样?
1、如果这个异常没有被捕获的话,这个线程就停止执行了。
2、如果该异常被捕获或抛出,则程序继续运行。
另外重要的一点是:如果这个线程持有某个某个对象的监视器,那么这个对象监视器会被立即释放
31、如何在两个线程之间共享数据?
通过在线程之间共享对象就可以了,然后通过wait/notify/notifyAll、await/signal/signalAll进行唤起和等待,比方说阻塞队列BlockingQueue就是为线程之间共享数据而设计的
32、ThreadLocal有什么用?
ThreadLocal是Java里一种特殊的变量。每个线程都有一个ThreadLocal就是每个线程都拥有了自己独立的一个变量,竞争条件被彻底消除了。
33、生产者消费者模型的作用是什么?
- 通过平衡生产者的生产能力和消费者的消费能力来提升整个系统的运行效率,这是生产者消费者模型最重要 的作用
- 解耦,这是生产者消费者模型附带的作用,解耦意味着生产者和消费者之间的联系少,联系越少制约就越少
34、为什么要使用线程池?
1、节约资源:避免频繁地创建和销毁线程,达到线程对象的重用。
2、灵活:使用线程池还可以根据项目灵活地控制并发的数目。
35、什么是线程组,为什么在Java中不推荐使用?
线程组和线程池是两个不同的概念,他们的作用完全不同。线程组是为了方便线程的管理。
线程池是为了管理线程的生命周期,复用线程,减少创建销毁线程的开销。
线程组ThreadGroup对象中的stop,resume,suspend会导致安全问题,主要是死锁问题,已经被官方废 弃,多以价值已经大不如以前。
线程组ThreadGroup不是线程安全的,在使用过程中不能及时获取安全的信息。
36、SynchronizedMap和ConcurrentHashMap有什么区别?
SynchronizedMap一次锁住整张表来保证线程安全,所以每次只能有一个线程来访为map。
ConcurrentHashMap使用分段锁来保证在多线程下的性能。
强一致性:任何时刻线程读取到的缓存数据都是一样的.
弱一致性:不能保证任何一次读都能读到最近一次写入的数据,但能保证最终可以读到写入的数据 .
37、ConcurrentHashMap的并发度是什么
JDK1.7
ConcurrentHashMap把实际map划分成若干部分来实现它的可扩展性和线程安全。这种划分是使用并发度获得的,它是ConcurrentHashMap类构造函数的一个可选参数,默认值为16,这样在多线程情况下就能避免争用。
在JDK8后,它摒弃了Segment(锁段)的概念,而是启用了一种全新的方式实现,利用CAS算法。同时加入了更多 的辅助变量来提高并发度.
38、CopyOnWriteArrayList可以用于什么应用场景?
CopyOnWriteArrayList 是 java.util.concurrent 包提供的方法,它实现了读操作无锁,写操作则通过操作底层数组的新副本来实现,是一种读写分离的并发策略。
39、Thread.sleep(0)的作用是什么?
由于大部分操作系统采用抢占式的线程调度算法,因此可能会出现某条线程常常获取到CPU控制权的情况,为了让 某些优先级比较低的线程也能获取到CPU控制权,可以使用Thread.sleep(0)手动触发一次操作系统分配时间片的操作,这也是平衡CPU控制权的一种操作。
大循环里面可以写一句Thread.Sleep(0) ,因为这样就给了其他线程获得CPU控制权的权力,这样有些线程就不会假死的状况。
比如:RocketMQ源码中的Thread.sleep(0)
40、Thread类中的yield方法有什么作用?
线程的状态主要有以下五种:
“新建”(NEW)、
“就绪”(RUNNABLE)、
“运行”(RUNNING)、
“阻塞”(BLOCKED)、
“ 死 亡 ”(DEAD)
yield方法使当前线程从执行状态(运行状态)变为可执行态(就绪状态)。
41、什么是线程调度器和CPU时间片?
线程调度器是一个操作系统服务,它负责为Runnable状态的线程分配CPU时间。一旦我们创建一个线程并启动 它,它的执行便依赖于线程调度器的实现。
CPU 时间片是 CPU 分配给每个线程执行的时间段,一般为几十毫秒。
时间分片是指将可用的CPU时间分配给可用的Runnable线程的过程。分配CPU时间可以基于线程优先级或者线程等待的时间。
42、为什么代码会重排序?
在不影响单线程程序执行结果的前提下,计算机为了最大限度的发挥机器性能,会对指令重排序优化
43、如果你提交任务时,线程池队列已满。会时发会生什么?
44、 Java中的ReadWriteLock是什么?
ReadWriteLock接口的实现ReentrantReadWriteLock
ReentrantLock等大部分锁都是排他锁,这些锁在同一时刻只允许一个线程进行访问,而读写锁在同一时刻可以允 许多个读线程访问,但是在写线程访问时,所有的读线程和其他写线程均被阻塞。读写锁维护了一对锁,一个读锁 和一个写锁,通过分离读锁和写锁,使得并发性相比一般的排他锁有了很大提升。
一般情况下,读写锁的性能都会比排它锁好,因为大多数场景读是多于写的。在读多于写的情况下,读写锁能够提 供比排它锁更好的并发性和吞吐量。
45、volatile 变量和 atomic 变量有什么不同?
Volatile修改变量可以确保可见性,同时禁止指令重排序,但它并不能保证原子性。例如用volatile修饰count变量那么 count++ 操作就不是原子性的。
而atomic变量,例如AtomicInteger类提供的这种如getAndIncrement()方法操作具有原子性。
46、CyclicBarrier和CountDownLatch的区别?
Countdownlatch是一个同步工具类;用来协调多个线程之间的同步;
这个工具通常用来控制线程等待;它可以让某一个线程等待知道倒计时结束,在开始执行;
CyclicBarrier和CountDownLatch非常类似,他也可以实现线程间的技术等待。 区别:CyclicBarrier可以重复使用,而CountdownLatch不能重复使用。
47、什么是AQS?
是用来构建锁或者其他同步组件的基础框架,比如ReentrantLock、ReentrantReadWriteLock和
CountDownLatch就是基于AQS实现的。它使用了一个int成员变量表示同步状态,通过内置的FIFO队列来完成资源获取线程的排队工作。它是CLH队列锁的一种变体实现。它可以实现2种同步方式:独占式,共享式。
AQS的主要使用方式是继承,子类通过继承AQS并实现它的抽象方法来管理同步状态,同步器的设计基于模板方法 模式,所以如果要实现我们自己的同步工具类就需要覆盖其中几个可重写的方法,如tryAcquire、
tryReleaseShared等等。
48、SafePoint 是什么?安全区域是什么? 安全点
用户线程暂停,GC线程要开始工作,但是要确保用户线程暂停的这行字节码指令是不会导致引用关系的变化。所 以JVM会在字节码指令中,选一些指令,作为“安全点”,比如方法调用、循环跳转、异常跳转等,一般是这些指令 才会产生安全点。
为什么它叫安全点,是这样的,GC时要暂停业务线程,并不是抢占式中断(立马把业务线程中断)而是主动是中 断。
主动式中断是设置一个标志,这个标志是中断标志,各业务线程在运行过程中会不停的主动去轮询这个标志,一旦 发现中断标志为True,就会在自己最近的“安全点”上主动中断挂起。
安全区域
为什么需要安全区域?
要是业务线程都不执行(业务线程处于Sleep或者是Blocked状态),那么程序就没办法进入安全点,对于这种情 况,就必须引入安全区域。
安全区域是指能够确保在某一段代码片段之中, 引用关系不会发生变化,因此,在这个区域中任意地方开始垃圾收集都是安全的。我们也可以把安全区城看作被扩展拉伸了的安全点。
当用户线程执行到安全区域里面的代码时,首先会标识自己已经进入了安全区域,这段时间里JVM要发起GC就不必 去管这个线程了。
当线程要离开安全区域时,它要JVM是否已经完成了(根节点枚举,或者其他GC 中需要暂停用户线程的阶段)
1、如果完成了,那线程就当作没事发生过,继续执行。
2、否则它就必须一直等待, 直到收到可以离开安全区域的信号为止。
49、请介绍常见的JVM参数
1、标准: -开头,所有的HotSpot都支持 保证Java虚拟机(JVM)的所有实现都支持标准选项。它们用于执行常见操作,例如检查JRE版本,设置类路径,启用详细输出等
2、标准:-X 开头,特定版本HotSpot支持特定命令 非标准选项是特定于Java HotSpot虚拟机的通用选项,因此不能保证所有JVM实现都支持它们,并且它们可能会发生变化。这些选项以开头-X。 -Xms30m -Xmx30m -Xss1m
3、高级选项:以开头-XX: 这些是开发人员选项,用于调整Java HotSpot虚拟机操作的特定区域,这些区域通常具有特定的系统要求,并且可能需要对系统配置参数的特权访问。也不能保证所有JVM实现都支持它们,并且它们可 能会发生变化。
在windows上可以通过以下java -XX:+PrintFlagsFinal –version查询所有-XX的
50、G1垃圾回收器的特点
- 并发与并行
- 分代收集
- 空间整合,基于标记-整理算法,解决了内存碎片的问题。
- 可以建立可预测的停顿模型
- 将整个java堆内存模型划分为多个大小相等的Region,使得年轻代和老年代不再物理隔离开来
由于文章限制高并发面试题到这就结束了,希望能对正在面试的你有所帮助,领取完整版PDF资料“点赞收藏关注”扫描下方二维码即可自行下载