“我有七十二般变化,万劫长生之术,会驾筋斗云,一纵就是十万八千里,如何坐不得这天位?”🌹
目录
用于提交任务的execute()和submit()方法有什么区别?
话不多说,发车!
说说你对AQS的理解?
全称是AbstractQueuedSynchronizer,队列同步器,是用来构建锁或者同步器的基础框架
你知道AQS的原理是什么吗?
AQS核心思想是,如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并且将共享资源设置为锁定状态。
如果被请求的共享资源被占用,那么就需要一套线程阻塞等待以及被唤醒时锁分配的机制,这个机制AQS是用CLH队列实现的,线程会首先尝试获取锁,如果失败就将当前线程及等待状态等信息包装成一个node节点加入到同步队列,接着会不断的循环尝试获取锁,如果失败就会阻塞自己直到自己被唤醒。而当持有锁的线程释放锁的时候,会唤醒队列中的后继线程。
AQS对资源的共享模式有哪些?
- 独占式:只允许一个线程获取同步状态,当这个线程还没有释放同步状态时,其他线程是获取不了的,只能加入到同步队列,进行等待(如ReentrantLock)
- 共享式:允许多个线程同时获取到同步状态(semaphore、CountDownLatch等)
AQS中有哪些需要重写的方法?
package juc.aqs;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
public class MyAQS {
public static void main(String[] args) {
AbstractQueuedSynchronizer aqs = new AbstractQueuedSynchronizer() {
@Override
protected boolean tryAcquire(int arg) {
return super.tryAcquire(arg);
}
@Override
protected boolean tryRelease(int arg) {
return super.tryRelease(arg);
}
@Override
protected int tryAcquireShared(int arg) {
return super.tryAcquireShared(arg);
}
@Override
protected boolean tryReleaseShared(int arg) {
return super.tryReleaseShared(arg);
}
@Override
protected boolean isHeldExclusively() {
return super.isHeldExclusively();
}
};
}
}
- tryacquire(int):独占式获取资源,成功返回true,否则返回false
- tryrelease(int):独占式释放资源,成功返回true,否则返回false
- tryacquireshared(int):共享式获取资源,返回大于等于0的值表示成功,否则返回false
- tryreleaseshared(int):共享式释放资源,成功返回true,否则返回false
- isheldexclusive():该线程是否在独占资源
使用线程池有哪些好处?⭐
- 降低资源消耗:通过重复利用已经创建的线程来降低创建和销毁线程所造成的消耗
- 提高响应速度:当任务到达时,可以不需要等到线程创建就能立即执行
- 便于统一管理线程:线程是一种宝贵的资源,如果无限制的进行创建会消耗大量资源,也会降低系统的稳定性,使用线程池来管理可以便于统一分配、调优和监控
创建线程池的参数有哪些?⭐
* @param corePoolSize the number of threads to keep in the pool, even
* if they are idle, unless {@code allowCoreThreadTimeOut} is set
* @param maximumPoolSize the maximum number of threads to allow in the
* pool
* @param keepAliveTime when the number of threads is greater than
* the core, this is the maximum time that excess idle threads
* will wait for new tasks before terminating.
* @param unit the time unit for the {@code keepAliveTime} argument
* @param workQueue the queue to use for holding tasks before they are
* executed. This queue will hold only the {@code Runnable}
* tasks submitted by the {@code execute} method.
- corePoolSize(线程池的基本大小):当提交一个任务到线程池时,线程池会创建一个线程来执行任务,即使其他空闲的基本线程能够执行新任务也会创建线程,等到需要执行的任务数大于线程池基本大小时就不再创建
- runnableTaskQueue(任务队列):例如ArrayBlockingQueue:是一个基于数组结构的有界阻塞队列,此队列按 FIFO(先进先出)原则对元素进行排序;LinkedBlockingQueue:一个基于链表结构的阻塞队列,此队列按 FIFO 排序元素
- maximumPoolSize(线程池最大数量):线程池允许创建的最大线程数,如果队列满了,并且已创建的线程数小于最大线程数,则线程池会在创建新的线程执行任务
- keepaliveTime(线程活动保持时间):线程池的工作线程空闲后,保持存活的时间
- TimeUnit(线程保持活动时间的单位):天、小时、分钟等
- ThreadFactory(线程工厂):通过线程工厂给每个线程赋予名字
- RejectedExcutionHandler(饱和策略):如果线程池已经被任务占满,需要选择一个策略处理提交新的任务
线程池中线程数一般怎么设置?需要考虑哪些因素?
- 任务的性质:计算密集型的任务比较占 cpu,所以一般线程数设置的就比较小;但 IO 型任务主要时间消耗在 IO 等待上,cpu 压力并不大,所以线程数一般设置较大
- CPU使用率:第一,线程的初始化,切换,销毁等操作会消耗不小的 cpu 资源,使得 cpu 利用率一直维持在较高水平。第二,线程数较大时,任务会短时间迅速执行,任务的集中执行也会给 cpu 造成较大的压力
如何创建线程池?⭐
通过 ThreadPoolExecutor 的构造方法实现
通过 Executor 框架的工具类 Executors 来实现,可以创建三种类型的 ThreadPoolExecutor
package juc.aqs;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class MyAQS {
public static void main(String[] args) {
ExecutorService executorService = Executors.newSingleThreadExecutor();
ExecutorService executorService1 = Executors.newFixedThreadPool(1);
ExecutorService executorService2 = Executors.newCachedThreadPool();
}
}
用于提交任务的execute()和submit()方法有什么区别?
- execute用于提交不需要返回值的任务,所以无法判断任务是否被线程池执行成功
- submit用于提交需要返回值的任务,会返回一个future类型的对象,通过这个对象可以判断任务是否执行成功。通过调用future的get方法可以获取返回值,get方法会阻塞当前线程直到任务完成
整理面经不易,觉得有帮助的小伙伴点个赞吧~感谢收看!