1.使用线程池的好处
- 降低资源消耗:通过重复已创建的线程降低线程创建和销毁造成的的损耗;
- 提高响应速度:当任务到达的时候,任务可以不需要等待线程创建就能够执行。
- 提高线程的可管理性:线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一分配、调优、监控
2.实现Runnable接口和Callable接口的区别
Runnable接口不会返回结果和抛出检查异常,但是Callable接口可以。如果任务不需要返回结果或抛出异常推荐使用Runnable接口
3.执行excute方法和submit方法的区别是什么
- execute()方法用于提交不需要返回值的任务,所以无法判断任务是否被线程池执行成功与否。
- submit()方法用于提交需要返回值的任务。线程池会返回一个Future类型的对象,通过这个Future独对象判断任务是否执行成功,并且可以通过Future的get()方法获取返回值,get()方法会阻塞当前线程直到任务完成
3.cyclicbarrier和countdownlatch的区别
这两者都能实现线程之间的等待,只不过它们侧重点不同
- countdownlatch一般用于某个线程A等待若干个其他线程执行完任务后,他才执行。可以用CompleteableFuture类来改进。
- cyclicbarrier一般用于一组线程相互等待至某个状态,然后这一组线程再同时执行;
- 另外countdownlatch是不能够重用的,cyclicbarrier是可以重用的。
4.如何创建线程池?
- 通过构造方法实现:ThreadPoolExcutor
- 通过Executor框架的工具类Executors来实现
5.线程池的种类
Java通过Executors提供四种线程池:
- newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
- newFixedThreadPool 创建一个指定工作数量的线程池,可控制线程最大并发数,超出的线程会在队列中等待,
- newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行
- newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序执行。
线程池常用的阻塞队列:
6.线程池运行流程,参数,策略
线程池主要就是指定线程池核心线程数大小,最大线程数,存储的队列,拒绝策略,空闲线程存活时长。
- 当需要任务大于核心线程数的时候,就开始把任务存储在存储任务的队列里,
- 当存储队列满了的话,就开始增加线程池的线程数量,
- 如果当线程数量也达到了最大,就开始执行拒绝策略,比如说记录日志,直接丢弃,或者丢弃最老的任务。
线程池拒绝策略:
在使用线程池并且使用有界队列的时候,如果队列满了,任务添加到线程池的时候就会有问题,针对这些问题java线程池提供了以下几种策略
- AbortPolicy
- DiscardPolicy
- DiscardOldestPolicy
- CallerRunsPolicy
- 自定义
7.启动线程有哪几种方式
一。继承Thread类的子类创建线程类
- 定义Thread类的子类,并重写该类的run方法,该run方法的方法体就代表了线程要完成的任务。因此把run()方法称为执行体
- 创建Thread子类的实例,即创建了线程对象。
- 调用线程对象的start()方法来启动该线程。
二。通过Runnable接口创建线程类
- 定义runnable接口的实现类,并且重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体。
- 创建runnable实现类的实例,并依此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象,
- 调用线程对象的start()方法来启动线程。
三。通过Callable和Future创建线程
- 创建callable接口的实现类,并实现call()方法,该call()方法将作为线程执行体,并且有返回值。
- 创建callable实现类的实例,使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法返回值。
- 使用FutureTask对象作为Thread对象的target创建并启动线程。
- 调用FutureTask对象的get()方法来获得子线程执行结束后的返回值。
8. 说出你所知道的线程同步的方法?
- wait()使一个线程处于等待状态,并且释放所持有的对象的lock
- sleep()使一个正在运行的状态处于休眠状态,是一个静态方法,调用此方法要捕捉InterruptException异常
- notify()唤醒一个处于等待状态的线程,注意的是在调用此方法的时候,并不能确定的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而不是按照优先级。
- notifyAll()唤醒所有处于等待状态的线程,注意并不是所有唤醒线程一个对象的锁,而是让他们竞争
9.当一个线程进入一个对象的synchronized方法A之后,其他线程是否可进入此对象的synchronized()方法B?
不能,其他线程只能访问该对象的非同步方法,同步方法则不能进入。因为非静态方法上的synchronized修饰符要求执行方法时获取对象的锁,如果已经进入A方法说明对象锁已经被取走,那么试图进入B方法的线程就只能在等待池中等待对象的锁。
10.源码中的线程池是怎么复用线程的:
源码中的ThreadPoolExecutor中有个内置对象Worker,每个worker都是一个线程,worker线程数量和参数有关,每个worker会while死循环从阻塞队列中取数据,通过置换worker中Runnable对象,运行其run方法起到线程置换的效果,这样做的好处是避免多线程频繁线程切换,提高程序运行性能
11.如何合理配置线程池参数?
自定义线程池就需要我们自己配置最大线程数maximumPoolsize,为了高效的并发运行,这时需要看我们的业务是IO密集还是CPU密集。
- CPU密集型:是该任务需要最大的运算,而没有阻塞,CPU一直全速运行。
- IO密集型:即该任务需要大量的IO,即大量的阻塞。在单线程上运行IO密集型的任务会导致大量的CPU运算能力浪费在等待。
12.Exector和Executors的区别?
Executors工具类的不同方法按照我们的需求创建了不同的线程池,来满足业务的需求。
Exector接口对象能够执行我们的线程任务。ExecutorService接口继承了Exector接口并进行了扩展,提供了更多的办法我们能获得任务执行的状态并且可以获取任务的返回值。
使用ThreadPoolExecutor可以创建自定义线程池。Future表示异步计算的结果,他提供了检查计算是否完成的方法,以等待计算的完成,并可以使用get()方法获得计算的结果
.AQS原理概览
AQS的核心思想是,如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并且将共享资源设置为锁定状态。如果被请求的共享资源被占用,那么就需要一套线程阻塞的等待以及被唤醒时锁分配机制,这个机制AQS是用CLH队列锁实现的,即将暂时获取不到锁的线程加入到队列中。
13.什么是AQS
- AQS是一个锁框架,它定义了锁的实现机制,并开放出扩展的地方,让子类去实现。
- AQS底层是由同步队列+条件队列联手组成,同步队列管理着获取不到锁的线程的排队和释放;条件队列是在一定场景下,对同步队列的补充。
- AQS围绕两个队列,提供了四大场景,分别是:获得锁、释放锁、条件队列的阻塞,条件队列的唤醒,分别对应着AQS架构图的四种颜色的线的走向
14.AQS使用了哪些设计模式?
- 使用者继承AbstractQueuedSynchronized并重写指定的方法。(这些重写的方法很简单,无非是对于共享资源的获取和释放)
- 将AQS组合在自定义同步组件的实现中,并调用其模板方法,而这些模板方法会调用使用者重写的方法。
15.AQS中同步队列的数据结构?
- 当前线程获取同步状态失败,同步器将当前线程机等待状态等信息构造成一个Node节点加入队列,放在队尾,同步器重新设置尾节点。
- 加入队列后,会阻塞当前线程
- 同步状态被释放并且同步器重新设置首节点,同步器唤醒队列中的第一个节点,让其再次获取同步状态
16.AQS对资源的共享方式
- Exclusive(独占):只有一个线程能够执行,如ReentrantLock。又可分为公平锁和非公平锁
- Share(共享):多个线程可同时执行,如CountDownLatch、Semaphore、CountDownLatch、CyclicBarrier
读写锁:基于QAS,int的高16为读状态,低16位为写状态。
17.AQS组件了解吗?
- semaphore(信号量)-允许多个线程同时访问
- CountDownLatch(倒计时器):是一个同步工具类,用来协调多个线程之间的同步。
- CylicBarrier(循环栅栏):可循环使用(Cyclic)的屏障(Barrier)。要做的事情是,让一组线程到达一个屏障(也可以叫做同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续干活。
18.介绍一下Atomic原子类
Atmotic是指一个操作是不可中断的。即使是在多个线程一起执行的时候,一个操作一旦开始,就不会被其他线程干扰。
19.简单介绍一下AtomicInteger类的原理
AtomicInteger类主要用CAS和volatile和native方法来保证原子操作,从而避免synchronized的高开销,执行效率大为提升。