1. 什么是阻塞队列?阻塞队列的实现原理是什么?
阻塞队列(BlockingQueue)
是一个支持两个附加操作的队列。
这两个附加的操作是:在队列为空时,获取元素的线程会等待队列变为非空。当队列满时,存储元素的线程会等待队列可用。
阻塞队列常用于生产者和消费者的场景,生产者是往队列里添加元素的线程,消费者是从队列里拿元素的线程。阻塞队列就是生产者存放元素的容器,而消费者也只从容器里拿元素。
2. 什么是 Callable 和 Future?
Callable
接口类似于 Runnable
,从名字就可以看出来了,但是Runnable
不会返回结果,并且无法抛出返回结果的异常,而 Callable
功能更强大一些,被线程执行后,可以返回值,这个返回值可以被Future
拿到,也就是说,Future
可以拿到异步执行任务的返回值。可以认为是带有回调的 Runnable
。
Future
接口表示异步任务,是还没有完成的任务给出的未来结果。所以说 Callable 用于产生结果,Future 用于获取结果。
3. 什么是 FutureTask?
在 Java 并发程序中FutureTask
表示一个可以取消的异步运算。它有启动和取消运算、查询运算是否完成和取回运算结果等方法。只有当运算完成的时候结果才能取回,如果运算尚未完成 get
方法将会阻塞。一个FutureTask
对象可以对调用了 Callable
和 Runnable
的对象进行包装,由于FutureTask
也是调用了 Runnable
接口所以它可以提交给 Executor 来执行。
4. notify ()和 notifyAll ()有什么区别?
当一个线程进入 wait 之后,就必须等其他线程notify/notifyall
,使用 notifyall
,可以唤醒所有处于 wait 状态的线程,使其重新进入锁的争夺队列中,而 notify
只能唤醒一个。
如果没把握,建议notifyAll
,防止notify
因为信号丢失而造成程序异常。
5. 什么是可重入锁( ReentrantLock )?谈谈它的实现
线程可以重复进入任何一个它已经拥有的锁所同步着的代码块,synchronized、ReentrantLock
都是可重入的锁。在实现上,就是线程每次获取锁时判定如果获得锁的线程是它自己时,简单将计数器累积即可,每 释放一次锁,进行计数器累减,直到计算器归零,表示线程已经彻底释放锁。
6. 乐观锁和悲观锁的理解及如何实现,有哪些实现方式?
悲观锁:总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁。Java里面的同步原语synchronized
关键字的实现是悲观锁。
乐观锁:顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。在Java中原子变量类就是使用了乐观锁的一种实现方式 CAS 实现的。
乐观锁的实现方式:
使用版本标识来确定读到的数据与提交时的数据是否一致。提交后修改版本标识,不一致时可以采取丢弃和再次尝试的策略。
java 中的 Compare and Swap
即 CAS ,当多个线程尝试使用 CAS 同时更新同一个变量时,只有其中一个线程能更新变量的值,而其它线程都失败,失败的线程并不会被挂起,而是被告知这次竞争中失败,并可以再次尝试。Java开发交流君样:756584822
7. 什么是 CAS 操作,缺点是什么?
CAS 的基本思路就是,如果这个地址上的值和期望的值相等,则给其赋予新值,否则不做任何事儿,但是要返回原值是多少。每一个 CAS 操作过程都包含三个运算符:一个内存地址 V,一个期望的值 A 和一个新值 B,操作的时候如果这个地址上存放的值等于这个期望的值 A,则将地址上的值赋为新值 B,否则不做任何操作。
CAS 缺点
ABA 问题
比如说一个线程one从内存位置V中取出A
,这时候另一个线程two
也从内存中取出 A
,并且two
进行了一些操作变成了 B
,然后 two
又将V
位置的数据变成A,这时候线程 one
进行 CAS
操作发现内存中仍然是 A
然后one
操作成功。尽管线程 one 的 CAS 操作成功,但可能存在潜藏的问题。从 Java1.5
开始J DK 的 atomic
包里提供了一个类 AtomicStampedReference
来解决 ABA 问题。
循环时间长开销大:
对于资源竞争严重(线程冲突严重)的情况,CAS自旋的概率会比较大,从而浪费更多的 CPU 资源,效率低于 synchronized
。
只能保证一个共享变量的原子操作:
当对一个共享变量执行操作时,我们可以使用循环 CAS 的方式来保证原子操作,但是对多个共享变量操作时,循环 CAS 就无法保证操作的原子性,这个时候就可以用锁。
8. SynchronizedMap 和 ConcurrentHashMap 有什么区别?
1. Executor
框架是一个根据一组执行策略调用,调度,执行和控制的异步任务的框架。每次执行任务创建线程 new
Thread()
比较消耗性能,创建一个线程是比较耗时、耗资源的。
- 调用 new
Thread()
创建的线程缺乏管理,而且可以无限制的创建,线程之间的相互竞争会导致过多占用系统资源而导致系统瘫痪,还有线程之间的频繁交替也会消耗很多系统资源。
接使用 new Thread()
启动的线程不利于扩展,比如定时执行、定期执行、定时定期执行、线程中断等都不便实现。
9. 在 Java 中 wait 和 sleep 方法的不同?
最大的不同是在等待时 wait 会释放锁,而 sleep
一直持有锁。Wait
通常被用于线程间交互,sleep
通常被用于暂停执行。
10. 一个线程运行时发生异常会怎样?
如果异常没有被捕获该线程将会停止执行。Thread.UncaughtExceptionHandler
是用于处理未捕获异常造成线程突然中断情况的一个内嵌接口。当一个未捕获异常将造成线程中断的时候 JVM 会使用 Thread.getUncaughtExceptionHandler ( )
来查询线程的UncaughtExceptionHandler
并将线程和异常作为参数传递给handler
的uncaughtException()
方法进行处理。
生命不止坚毅鱼奋斗,有梦想才是有意义的追求
给大家推荐一个免费的学习交流群:
最后,祝大家早日学有所成,拿到满意offer,快速升职加薪,走上人生巅峰。
Java开发交流君样:756584822