Java面试题及答案(2022版),适用于应届生、有工作经验的程序员,每道都是认真筛选出的高频面试题,助力大家能找到满意的工作!
Java多线程
下载链接:全部面试题及答案PDF
1. 什么是线程?
线程是操作系统能够进⾏运算调度的最⼩单位,它被包含在进程之中,是进程中的实际运作单位,可以使⽤多线程对
进⾏运算提速。
⽐如,如果⼀个线程完成⼀个任务要100毫秒,那么⽤⼗个线程完成改任务只需10毫秒
2. 并行和并发有什么区别?
• 并行:多个处理器或多核处理器同时处理多个任务。
• 并发:多个任务在同一个 CPU 核上,按细分的时间片轮流(交替)执行,从逻辑上来看那些任务是同时执行。
3. 线程和进程的区别?
一个程序下至少有一个进程,一个进程下至少有一个线程,一个进程下也可以有多个线程来增加程序的执行速度。
4. 守护线程是什么?
守护线程是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。在 Java 中垃圾回收线程就是特殊的守护线程。
5. 什么是线程安全和线程不安全?
1 、线程安全
线程安全: 就是多线程访问时,采⽤了加锁机制,当⼀个线程访问该类的某个数据时,进⾏保护,其他线程不能进⾏ 访问,直到该线程读取完,其他线程才可使⽤。不会出现数据不⼀致或者数据污染。
Vector 是⽤同步⽅法来实现线程安全的, ⽽和它相似的ArrayList不是线程安全的。
2 、线程不安全
线程不安全:就是不提供数据访问保护,有可能出现多个线程先后更改数据造成所得到的数据是脏数据 线程安全问题都是由全局变量及静态变量引起的。
若每个线程中对全局变量、静态变量只有读操作,⽽⽆写操作,⼀般来说,这个全局变量是线程安全的;若有多个
线程同时执⾏写操作,⼀般都需要考虑线程同步,否则的话就可能影响线程安全。
6. 创建线程有哪几种方式?
创建线程有三种方式:
• 继承 Thread 重写 run 方法;
• 实现 Runnable 接口;
• 实现 Callable 接口。
7. 说一下 runnable 和 callable 有什么区别?
runnable 没有返回值,callable 可以拿到有返回值,callable 可以看作是 runnable 的补充。
8. 线程有哪些状态?
线程的状态:
• NEW 尚未启动
• RUNNABLE 正在执行中
• BLOCKED 阻塞的(被同步锁或者IO锁阻塞)
• WAITING 永久等待状态
• TIMED_WAITING 等待指定的时间重新被唤醒的状态
• TERMINATED 执行完成
9. 什么是CAS?
1 、 CAS (compare and swap)的缩写,中⽂翻译成⽐较并交换。
2 、 CAS 不通过JVM,直接利⽤java本地⽅ JNI (Java Native Interface为JAVA本地调⽤) ,直接调⽤ CPU 的cmpxchg (是 汇编指令)指令。
3、利⽤CPU的CAS指令,同时借助JNI来完成Java的⾮阻塞算法,实现原⼦操作。其它原⼦操作都是利⽤类似的特性完成 的。
4 、整个java.util.concurrent都是建⽴在CAS之上的,因此对于synchronized阻塞算法, J.U.C在性能上有了很⼤的提升。
5 、 CAS是项乐观锁技术 ,当多个线程尝试使⽤CAS同时更新同⼀个变量时,只有其中⼀个线程能更新变量的值,⽽其它线程都失败,失败的线程并不会被挂起,⽽是被告知这次竞争中失败,并可以再次尝试。
使⽤CAS在线程冲突严重时,会⼤幅降低程序性能;CAS只适合于线程冲突较少的情况使⽤。
synchronized在jdk1.6之后,已经改进优化。synchronized的底层实现主要依靠Lock-Free的队列,基本思路是⾃旋后阻塞,竞 争切换后继续竞争锁,稍微牺牲了公平性,但获得了⾼吞吐量。 在线程冲突较少的情况下,可以获得和CAS类似的性能;⽽线 程冲突严重的情况下,性能远⾼于CAS。
10. sleep() 和 wait() 有什么区别?
• 类的不同:sleep() 来自 Thread,wait() 来自 Object。
• 释放锁:sleep() 不释放锁;wait() 释放锁。
• 用法不同:sleep() 时间到会自动恢复;wait() 可以使用 notify()/notifyAll()直接唤醒。
11. notify()和 notifyAll()有什么区别?
notifyAll()会唤醒所有的线程,notify()之后唤醒一个线程。notifyAll() 调用后,会将全部线程由等待池移到锁池,然后参与锁的竞争,竞争成功则继续执行,如果不成功则留在锁池等待锁被释放后再次参与竞争。而 notify()只会唤醒一个线程,具体唤醒哪一个线程由虚拟机控制。
12. 线程的 run() 和 start() 有什么区别?
start() 方法用于启动线程,run() 方法用于执行线程的运行时代码。run() 可以重复调用,而 start() 只能调用一次。
13. 创建线程池有哪几种方式?
线程池创建有七种方式,最核心的是最后一种:
• newSingleThreadExecutor():它的特点在于工作线程数目被限制为 1,操作一个无界的工作队列,所以它保证了所有任务的都是被顺序执行,最多会有一个任务处于活动状态,并且不允许使用者改动线程池实例,因此可以避免其改变线程数目;
• newCachedThreadPool():它是一种用来处理大量短时间工作任务的线程池,具有几个鲜明特点:它会试图缓存线程并重用,当无缓存线程可用时,就会创建新的工作线程;如果线程闲置的时间超过 60 秒,则被终止并移出缓存;长时间闲置时,这种线程池,不会消耗什么资源。其内部使用 SynchronousQueue 作为工作队列;
• newFixedThreadPool(int nThreads):重用指定数目(nThreads)的线程,其背后使用的是无界的工作队列,任何时候最多有 nThreads 个工作线程是活动的。这意味着,如果任务数量超过了活动队列数目,将在工作队列中等待空闲线程出现;如果有工作线程退出,将会有新的工作线程被创建,以补足指定的数目 nThreads;
• newSingleThreadScheduledExecutor():创建单线程池,返回 ScheduledExecutorService,可以进行定时或周期性的工作调度;
• newScheduledThreadPool(int corePoolSize):和newSingleThreadScheduledExecutor()类似,创建的是个 ScheduledExecutorService,可以进行定时或周期性的工作调度,区别在于单一工作线程还是多个工作线程;
• newWorkStealingPool(int parallelism):这是一个经常被人忽略的线程池,Java 8 才加入这个创建方法,其内部会构建ForkJoinPool,利用Work-Stealing算法,并行地处理任务,不保证处理顺序;
• ThreadPoolExecutor():是最原始的线程池创建,上面1-3创建方式都是对ThreadPoolExecutor的封装。
14. 什么是乐观锁和悲观锁?
1 、悲观锁
Java在JDK1.5之前都是靠synchronized关键字保证同步的,这种通过使⽤⼀致的锁定协议来协调对共享状态的访 问,可以确保⽆论哪个线程持有共享变量的锁,都采⽤独占的⽅式来访问这些变量 。独占锁其实就是⼀种悲观锁,所以 可以说synchronized是悲观锁。
2 、乐观锁
乐观锁( Optimistic Locking)其实是⼀种思想。相对悲观锁⽽⾔, 乐观锁假设认为数据⼀般情况下不会造成冲突, 所以在数据进⾏提交更新的时候,才会正式对数据的冲突与否进⾏检测 ,如果发现冲突了,则让返回⽤户错误的信息,
让⽤户决定如何去做。
memcached使⽤了cas乐观锁技术保证数据⼀致性。
15. 什么是Executors框架?
Java通过Executors提供四种线程池,分别为:
1 、 newCachedThreadPool创建⼀个可缓存线程池, 如果线程池⻓度超过处理需要,可灵活回收空闲线程,若⽆可 回收,则新建线程。
2 、 newFixedThreadPool 创建⼀个定⻓线程池, 可控制线程最⼤并发数,超出的线程会在队列中等待。
3 、 newScheduledThreadPool 创建⼀个定⻓线程池, ⽀持定时及周期性任务执⾏。
4 、 newSingleThreadExecutor 创建⼀个单线程化的线程池, 它只会⽤唯⼀的⼯作线程来执⾏任务,保证所有任务 按照指定顺序(FIFO, LIFO, 优先级)执⾏。
16. 什么是阻塞队列?如何使⽤阻塞队列来实现⽣产者-消费者模型?
1 、 JDK7提供了7个阻塞队列 。(也属于并发容器)
i. ArrayBlockingQueue :⼀个由数组结构组成的有界阻塞队列。
ii. LinkedBlockingQueue :⼀个由链表结构组成的有界阻塞队列。
iii. PriorityBlockingQueue :⼀个⽀持优先级排序的⽆界阻塞队列。
iv. DelayQueue:⼀个使⽤优先级队列实现的⽆界阻塞队列。
v. SynchronousQueue:⼀个不存储元素的阻塞队列。
vi. LinkedTransferQueue:⼀个由链表结构组成的⽆界阻塞队列。
vii. LinkedBlockingDeque:⼀个由链表结构组成的双向阻塞队列。
2、概念: 阻塞队列是⼀个在队列基础上⼜⽀持了两个附加操作的队列。
3 、 2个附加操作:
⽀持阻塞的插⼊⽅法:队列满时,队列会阻塞插⼊元素的线程,直到队列不满。
⽀持阻塞的移除⽅法:队列空时,获取元素的线程会等待队列变为⾮空。