java多线程、并发面试题

  1. Java中CyclicBarrier 和 CountDownLatch有什么不同?
    CountDownLatch 的计数器只能使用一次,而CyclicBarrier 的计数器可以使用reset() 方法重置,所以后者能处理更为复杂的业务场景;CountDownLatch 是一个(或多个)线程等待另外N个执行任务的线程,而CyclicBarrier 是N个线程互相等待,任何一个线程完成之前,所有线程都必须等待;CyclicBarrier 的实现原理是,利用ReentrantLock 做线程安全锁,实现线程安全等待。
  2. Java中的volatile 变量是什么?
    java语言规范对volatile 的定义如下:Java编程语言允许线程访问共享变量,为了确保共享变量能被准确和一致的更新,线程应该确保通过排它锁单独获得这个变量。
    volatile 变量修饰的共享变量进行写操作时,汇编代码会多出一个Lock 前缀的指令,这个指令会引发两件事情:

    • 将当前处理器缓存行的数据写回到系统内存。

    • 这个写回内存的操作会使在其他CPU 里缓存了该内存地址的数据无效。

两个特性:内存可见性和禁止指令重排序。
可见性:当写一个volatile 变量时,JVM会把该线程对应的本地内存中的共享变量值刷新到主内存;而读一个volatile 变量时,JVM会把该线程对应的本地内存置为无效,线程接下来将从主内存中读取共享变量。
禁止重排序: 编译器在生成字节码时,会在指令序列中插入内存屏障来禁止特定类型的处理器重排序。
3. Java内存模型是什么?
Java内存模型规定和指引Java程序在不同的内存架构、CPU和操作系统间有确定性地行为。它在多线程的情况下尤其重要。Java内存模型对一个线程所做的变动能被其它线程可见提供了保证,它们之间是先行发生关系。这个关系定义了一些规则让程序员在并发编程时思路更清晰。比如,先行发生关系确保了:

  • 线程内的代码能够按先后顺序执行,这被称为程序次序规则。
  • 对于同一个锁,一个解锁操作一定要发生在时间上后发生的另一个锁定操作之前,也叫做管程锁定规则。
  • 前一个对volatile的写操作在后一个volatile的读操作之前,也叫volatile变量规则。
  • 一个线程内的任何操作必需在这个线程的start()调用之后,也叫作线程启动规则。
  • 一个线程的所有操作都会在线程终止之前,线程终止规则。
  • 一个对象的终结操作必需在这个对象构造完成之后,也叫对象终结规则。
  • 可传递性

4、 一个线程运行时发生异常会怎样?
多线程运行不能按照顺序执行过程中捕获异常的方式来处理异常,异常会被直接抛出到控制台(由于线程的本质,使得你不能捕获从线程中逃逸的异常。一旦异常逃逸出任务的run方法,它就会向外传播到控制台,除非你采用特殊的形式捕获这种异常。),这样会让你很头疼,无法捕捉到异常就无法处理异常而引发的问题。
简单的说,如果异常没有被捕获该线程将会停止执行。Thread.UncaughtExceptionHandler是用于处理未捕获异常造成线程突然中断情况的一个内嵌接口。当一个未捕获异常将造成线程中断的时候JVM会使用Thread.getUncaughtExceptionHandler()来查询线程的UncaughtExceptionHandler并将线程和异常作为参数传递给handleruncaughtException()方法进行处理。
总结:在java中要捕捉多线程产生的异常,需要自定义异常处理器,并设定到对应的线程工厂中。
5. 什么是ThreadLocal变量?
ThreadLocal ,即线程变量,是一个以ThreadLocal 对象为键、任意对象为值的存储结构。这个结构被附带在线程上,也就是说一个线程可以根据一个ThreadLocal 对象查询到绑定在这个线程上的一个值。
ThreadLocal 实现线程隔离的秘密,在于ThreadLocalMap 类。它是ThreadLocal 的一个静态内部类,实现了键值对的设置和获取,每个线程都有一个独立的ThreadLocalMap副本,它所存储的值,只能被当前线程读取和修改。
我们可以创建不同的ThreadLocal 实例来实现多个变量在不同线程间的访问隔离(因为不同的ThreadLocal 对象作为不同键,就像你在Map 中设置多个键值对一样)。
ThreadLocal 在处理线程的局部变量的时候比synchronized 更简单,更方便,且结果程序拥有更高的并发性。
使用ThreadLocal ,一直都是声明在静态变量中,如果不断地创建ThreadLocal 而且没有调用其remove() 方法,将会导致内存泄漏,特别是在高并发的Web容器中。
6. 什么是FutureTask?
FeatureTask 作为Feature 接口的唯一实现类,代表异步计算的结果,同时也实现了Runnable 接口,因此,FeatureTask 可以交给Executor 执行,也可以由调用线程直接执行(FeatureTask.run())。它有启动和取消运算、查询运算是否完成和取回运算结果等方法。只有当运算完成的时候结果才能取回,如果运算尚未完成get方法将会阻塞。
包括:未启动、已启动、已完成三种状态。
7. Java中interrupted 和 isInterruptedd方法的区别?
interrupted()isInterrupted()的主要区别是前者会将中断状态清除而后者不会。Java多线程的中断机制是用内部标识来实现的,调用Thread.interrupt()来中断一个线程就会设置中断标识为true。当中断线程调用静态方法Thread.interrupted()来检查中断状态时,中断状态会被清零。而非静态方法isInterrupted()用来查询其它线程的中断状态且不会改变中断状态标识。简单的说就是任何抛出InterruptedException异常的方法都会将中断状态清零。无论如何,一个线程的中断状态有有可能被其它线程调用中断来改变。
8. 为什么你应该在循环中检查等待条件?
处于等待状态的线程可能会收到错误警报和伪唤醒,如果不在循环中检查等待条件,程序就会在没有满足结束条件的情况下退出。因此,当一个等待线程醒来时,不能认为它原来的等待状态仍然是有效的,在notify()方法调用之后和等待线程醒来之前这段时间它可能会改变。这就是在循环中使用wait()方法效果更好的原因。
9. 线程池的种类,区别和使用场景
线程池: 创建线程要花费昂贵的资源和时间,如果任务来了才创建线程那么响应时间会变长,而且一个进程能创建的线程数有限。为了避免这些问题,在程序启动的时候就创建若干线程来响应处理,它们被称为线程池,里面的线程叫工作线程。
分类:
newFixedThreadPool 将创建一个固定长度的线程池,每当提交一个任务时就创建一个线程,直到达到线程池的最大数量,这时线程池的规模将不再变化(如果某个线程由于发生了未预期的Exception 而结束,那么线程池将会补充一个新的线程)。
newCachedThreadPool 将创建一个可缓存的线程池,如果线程池的当前规模超过了处理需求,那么将回收空闲的线程,而当需求增加时,则可以添加新的线程,线程池的规模不存在任何的限制。
newSingleThreadExecutor 是一个单线程的Executor ,它创建单个工作者线程来执行任务,如果这个线程异常结束,会创建一个线程来替代他。它能确保依照任务在队列中的顺序来串行执行。
newScheduledThreadPool 创建了一个固定长度的线程池,而且以延迟或定时的方式来执行任务,类似于Timer
好处:
(1)降低资源消耗:通过重复利用已创建的线程,降低线程创建和销毁造成的消耗。
(2)提高响应速度:当任务到达时,任务可以不需要等到线程创建就能立即执行。
(3)提供线程的可管理性:线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配、调控和优化。但是要做到合理地利用线程池,必须对其原理深入了解。
(4)防止服务器过载,形成内存溢出,或者CPU耗尽。
如何提高服务器性能:多线程技术主要解决处理器单元内多个线程执行的问题,它可以显著减少处理器单元的闲置时间,增加处理器单元的吞吐能力。
应用范围:需要大量的线程来完成任务,且完成任务的时间比较短;对性能要求苛刻的应用;接受突发性的大量请求,但不至于使服务器因此产生大量线程的应用。
工作机制及其原理:

    线程池的核心的两个队列:
    **线程等待池:** 即线程队列`BlockingQueue`;
    **任务处理池:(`PoolWorker`)**,即正在工作的`Thread` 列表(`HashSet<Worker>`)。
    线程池的核心的参数:
    **核心池大小(`corePoolSize`):**,即固定大小,设定好之后,线程池的稳定峰值,达到这个值之后池的线程数大小不会释放。
    **最大处理线程池数(maximumPoolSize):**,当线程池里面的线程数超过`corePoolSize`,小于`maximumPoolSize` 时会动态地创建与回收线程池里面的线程的的资源。

实现原理:
当向线程池提交一个任务之后,处理流程:
(1)线程池判断核心线程池里的线程是否都在执行任务。如果不是,则创建一个新的工作线程来执行任务;如果是,则进入下个流程。
(2)线程池判断工作队列是否已满。如果没有满,则将新提交的任务存储在这个工作队列里;如果满了,进入下个流程。
(3)线程池判断线程池中的线程是否都处于工作状态。如果不是,则创建一个新的工作线程来执行任务;如果是,则交给饱和策略来处理这个任务。
工作队列类
ArrayBlockingQueue
LinkedBlockingQueue
SynchronousQueue
PriorityBlockingQueue
饱和策略:
AbortPolicy: 直接抛出异常。
CallerRunsPolicy: 只用调用者所在线程来运行任务。
DiscardOldestPolicy: 丢弃队列里最近的一个任务,并执行当前任务。
DiscardPolicy: 不处理,直接丢弃掉。
10. 用Java写代码来解决生产者——消费者问题。
待补充…………………
11. 用Java编程一个会导致死锁的程序,你将怎么解决?
待补充…………
12. 什么是竞争条件?你怎样发现和解决竞争?
13. 你将如何使用thread dump?你将如何分析Thread dump?
14. Java中你怎样唤醒一个阻塞的线程?
15. 什么是不可变对象,它对写并发应用有什么帮助?
16. 你在多线程环境中遇到的常见的问题是什么?你是怎么解决它的?
17. 死锁与活锁的区别,死锁与饥饿的区别?
18. Java中用到的线程调度算法是什么?
19. 在Java中什么是线程调度?
20. 为什么使用Executor框架比使用应用创建和管理线程好?
21. 在Java中Executor和Executors的区别?
22. 如何在Windows和Linux上查找哪个线程使用的CPU时间最长?

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

每天进步一点_点

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值