线程池使用的是哪种
ThreadPoolExecutor: 核心线程数、最大线程数、存活时间、ThreadFactory、BlockingQueue任务队列、RejectedExecutionHandler拒绝策略
4种拒绝策略:当队列(有界队列)任务已满时的处理方式
1、AbortPolicy,丢弃任务并抛出RejectedExecutionExecption
2、DiscardPolicy,丢弃任务但不抛出异常
3、DiscardOldestPolicy,丢弃队列最前面的任务,然后尝试重新执行任务
4、CallerRunsPolicy,由调用线程处理任务
线程池参数怎么配置
参上
线程池各个参数的作用
参上
线程池的参数配置要注意什么
创建线程池建议采用new ThreadPoolExecutor,而不是采用Executor提供的方法来创建,因为Executor提供的创建方式线程最大数量是Integer.MAX,可能会造成OOM。
线程池的工作流程
JDK 中的并发类知道哪些
Java并发包concurrent包下的:
1、Lock, 乐观锁操作
2、阻塞队列
3、原子操作类
4、线程池:Executors
5、信号量:CountDownLatch、CyclicBarrier等
AQS 的底层原理
介绍下悲观锁和乐观锁
使用过哪些锁?
syncronized
RenrtrrentLock:重入锁,sync和RentrentLock都是重入锁,重入锁是为了解决线程死锁问题。
CAS
synchronized 和 Lock 的区别、使用场景
1、sync是jvm层面处理的,是一个关键字,而Lock是一个类
当使用sync同步代码块的时候,可以通过查看class字节码看到:
```
public class SyncDemo{
public synchronized void test1(){
}
public void test2(){
synchronized (this){
}
}
}
```
2、sync是直接通过加锁,属于悲观锁,而Lock使用CAS机制,是乐观锁。
3、sync会自动释放锁,而lock需要手动释放,并且需要try、catch、finally中最终释放(防止死锁)
synchronized 原理
Java的对象存储主要分为以下三个部分:
对象头包括两部分数据:MarkWord以及类型指针(Class MetaData Address)指向当前对象的类元数据
MarkWord是非固定结构的数据,主要存储了对象的HashCode、锁状态、锁标记位等,根据对象的锁状态,锁存储的数据也有所不同。
JDK1.6之前,仅有重量级锁的概念,重量级锁也就是同步锁,会造成线程阻塞,并且会使线程在用户态与核心态频繁切换,影响效率。而在JDK1.6,JVM对synchnorized做了较大的优化,引入了偏向锁、轻量锁。对象不会一创建就处于重量级锁的状态,而是按照一定的规则升级。
锁升级过程:
1、对象在创建初期,此时没有任何线程竞争,lock的标记位是01,偏向锁标记位0
2、当有线程来竞争锁时,将升级为偏向锁,在标记字(MarkWord)中记录该线程的ID,此后该线程访问此对象时不需要再做任何检验和切换,此时的线程执行效率是非常高的。
3、当有两个线程来竞争锁时,将升级为轻量锁,即两个线程公平竞争锁,谁先获得锁就获得了代码的执行。MarkWord将不再记录偏向线程,而是记录栈帧中的锁记录。
4、当更多的线程来竞争锁时,轻量锁将升级为重量锁,此时MarkWord记录一个监视器的对象(ObjectMintor)
synchronized 作用于静态方法、普通方法、this、Lock.class 的区别
静态方法,锁的是this.class(类对象),普通方法锁的是this
为什么引入偏向锁、轻量级锁,介绍下升级流程
升级流程如上上个问题
死锁的必要条件,如何预防死锁
1、互斥条件:一个资源只能被一个线程占有
2、请求与保持条件:至少保持了一个占有资源,又去请求其它的资源,此时线程阻塞,但是对已获得的资源不释放
3、不可剥夺条件:线程已经获取的资源,不能被其它线程占有,只能等待当前线程主动释放
4、循环等待条件:若干线程形成收尾相接循环等待的关系
破坏死锁:由于线程的互斥是不可改变的,那么只能从其它三个方面去破坏死锁。
1、破坏不可剥脱条件:当发现不能获取执行所需的全部资源时,则处于等待状态,当可以获取到全部资源时,才执行。
2、破坏请求保持:
3、破坏循环等待:将资源进行编号,按照资源紧缺,越紧缺编号越大,只有当线程可以获取到编号最小的资源时,才执行。
介绍下 CountDownLatch 和 CyclicBarrier
两者都是用来做同步辅助控制的,CountDownLatch,执行的当前线程会处于阻塞状态,等到所有线程都执行完毕,才会进入下一步,不可循环。而CyclicBarrier执行的当前线程不会阻塞,是一种异步操作,而且是可循环的。
介绍下 CAS,存在什么问题
CAS,Compare And Swap 比较并替换。
是一种乐观锁,在更新数据的时候,将数据与内存中的数据进行比较,如果发现一致,则可以更新为新的值,不一致则获取最新的值并重新执行以上流程。
1、ABA问题,内存中的值可能经过A 修改为B 后又修改为A,如果仅仅比较值,可能是一样的,那么这种改变对程序是不可见的,可以通过加版本号解决,每次修改,将版本号加1
2、自旋问题:某个线程可能会一直处于自旋(循环)状态,此时的CPU开销是比较大的
介绍下 ThreadLocal,存在什么问题
同一个ThreadLocal对象在不同线程中有不同的数据副本,其数据结构采用ThreadLocalMap实现,每个线程创建的时候,set数据的时候,会有线程作为key,vlaue作为值存入ThreadLocalMap中
存在问题:
1、ThreadLocalMap的key采用的是弱引用,可以保证在在线程执行完毕之后key可以被回收,但是value是强引用,任然需要手动回收,否则可能会造成内存泄漏。
2、线程池数据错乱:因为现在使用的基本都是线程池,那么在不同的时间执行的方法使用的线程可能是同一个,如果在ThreadLocal中set了值而没有清理,则可能会造成数据错乱,使用remove来清理数据保证数据干净。
使用场景:
1、Spring采用ThreadLocal解决事务控制的问题:一个业务方法中,可能调用了很多个Mapper接口的方法,要实现事务控制,必须保证这些Mapper使用的数据库连接是同一个。 同时Spring的事务传播机制不同,事务的处理逻辑也是不一样的。比如:A方法调用B方法, A的事务处理与B的事务处理逻辑分开,那么A方法和B方法所使用的数据库连接则不能是同一个。
2、数据传输:一个变量在多个类、方法都使用到,要保证其线程安全,可以通过方法传递的方式,也可以通过ThreadLocal来实现。