并发问答
javajava大约 6 分钟
Java 的高并发容器有哪些?
- 同步容器:操作方法都加了synchronized
- 并发容器:
- List:CopyOnWriteArrayList
- Set:CopyOnWriteArraySet、ConcurrentSkipListSet
- Map:ConcurrentHashMap、ConcurrentSkipListMap
- Queue:BlockingQueue、BlockDeQueue、ConcurrentLinkedQueue、ConcurrentLinkedDeQueue
CAS
- 名词解释:比较并交换,比较共享变量的当前值与目标值是否一致,如果一致则进行交换
- 主要作用:原子性更新共享变量的值,保证线程安全
- 底层变量:变量当前值(V)变量预期值(A)变量新值(B)
- 判断条件:V=A时,才会设置V=B,否则不更新
- 返回结果:V更新前的值
- 线程安全:原子操作,执行期间不会被其他线程中断
CAS带来的问题
- 频繁自旋,开销大
- 只能保证1个共享变量的原子性操作
- ABA问题
ABA 问题?
A->B->A 此时cas认为A未发生变化,实际上已经发生了变化
解决方案:加入版本号记录
线程池的原理?
为什么要用线程池
- 降低资源消耗:线程创建销毁开销大,利用池化技术,减少创建线程的开销,做到线程复用
- 提高响应速度:现来现用,省去了从头创建线程的流程
- 优化线程管理:线程作为稀缺资源,线程池有合理的机制来管理和维护他的生命周期,并提供调优和监控
线程池的生命周期
- Running:接受新任务,并处理队列中的任务
- Shutdown:不接受新任务,继续处理队列中的任务
- Stop:不接受新任务,停止消费队列中的任务,中断正在处理中的任务
- Tidying:所有任务处理完成,有效线程数0
- Terminated:terminated()被调用
阻塞队列有哪几种?
- ArrayBlockQueue:基于数组的有界阻塞队列
- LinkedBlockQueue:基于链表
- SynchronousQueue:不存储元素,只做转交、传递(每个插入操作会一直阻塞到另一个线程调用移除操作)
- PrioityBlockQueue:优先级队列
线程池的拒绝策略有哪几种?
- CallReturnPolicy:交由主线程执行
- AbortPolicy:拒绝执行,并抛出异常(默认策略)
- DiscardPolicy:直接丢弃,无异常抛出
- DiscardOldPolicy:丢弃最老的任务,把当前任务加入队列
- 自定义....
多线程创建多少个线程合适?
Java 线程池有哪几种?
线程池如何监控?
线程池自带的API,通过定时任务刷新
什么是 AQS
定义了关于同步器的实现规范,即线程之间通过CAS竞争共享资源,如果未获取到共享资源,则可进入阻塞队列,AQS为所有人实现同步器提供了一个标准模版
ThreadLocal 有什么缺点?
线程本地变量,一旦遇上了线程池,如果没有正确使用ThreadLocal,容易出现内存泄漏或者逻辑bug
volatile 有什么特点,和 synchornized 相比有什么区别?
volatile 特性
- 保证可见性:当一个线程修改了共享变量,其他线程立即得知这一修改
- 防止指令重排:通过插入内存屏障指令,禁止单线程下出现指令重排
- 可以用在循环终止条件变量,或者单例对象创建事禁止指令重排
volatile如何实现的可见性?
- 当写一个 volatile 变量时,JMM 会把该线程对应的工作内存中的共享变量值刷新到主内存中;
- 当读取一个 volatile 变量时,JMM 会把该线程对应的工作内存置为无效.
- mesi缓存一致性协议:多个CPU从主内存读取同一个数据到各自得高速缓存,当其中某个cpu修改了缓存里得数据,该数据会马上同步回主内存,其他cpu通过总线嗅探机制可以感知到数据得变化从而将自己缓存里得数据失效, 而这其中,监听和通知又基于总线嗅探机制来完成
volatile 不保证原子性
与Synchronized的区别:
- volatile只能修饰变量,synchronized可修饰变量、方法、类
- volatile支持可见性、有序性,synchronized支持可见性、有效性、原子性
- volatile不会导致阻塞、synchroized会阻塞
Java 内存模型知道吗?
- why:屏蔽硬件和各种操作系统的内存访问差异
- what:JMM也叫Java内存模型,是一种抽象概念,是一组规范用来定义变量的访问方式
JMM内存模型中有两大内存:主内存和工作内存
- 主内存:即物理硬件内存,java堆中的对象实例数据
- 工作内存:即寄存器或者高速缓存,java栈中的部分区域
JMM的规范:
- 所有的变量存储于主内存中
- 主内存是虚拟机内存的一部分
- 每个线程都有独立的工作内存,且私有
- 线程的工作内存中保存的是某变量的:主内存的副本
- 线程对变量的操作必须在工作内存中进行
- 不同线程无法直接访问对方工作内存中的变量值
- 不同线程间的变量值传递必须通过主内存
Happens-Before
- 程序的顺序性规则:一个线程按照程序顺序,前面的操作对后面的操作可见
- volatile变量规则:一个对volatile变量的写操作,相对于之后volatile变量的读操作可见
- 传递性:A
happens-beforeB , Bhappens-beforeC,则Ahappens-beforeC
//
class VolatileExample {
int x = 0;
volatile boolean v = false;
public void writer() {
x = 42;
v = true;
}
public void reader() {
if (v == true) {
// 这里x会是多少呢?
}
}
}
//如果在低于 1.5 版本上运行,x 可能是 42,也有可能是 0;
//如果在 1.5 以上的版本上运行,x 就是等于 42。(因为Happens-before)
- 管程中的锁规则:一个锁的解锁
happens-before于后续对这个锁的加锁 - 线程start原则:主线程 A 启动子线程 B 后,子线程 B 能够看到主线程在启动子线程 B 前的操作
- 线程join原则:线程 A 中,调用线程 B 的 join() 并成功返回,那么线程 B 中的任意操作 Happens-Before 于该 join() 操作的返回
Thread B = new Thread(()->{
// 此处对共享变量var修改
var = 66;
});
// 例如此处对共享变量修改,
// 则这个修改结果对线程B可见
// 主线程启动子线程
B.start();
B.join()
// 子线程所有对共享变量的修改
// 在主线程调用B.join()之后皆可见
// 此例中,var==66
Powered by Waline v2.14.1