Java实习生------JUC并发编程(多线程)10道面试题打卡(AQS队列同步器、线程池)⭐⭐⭐

 “我有七十二般变化,万劫长生之术,会驾筋斗云,一纵就是十万八千里,如何坐不得这天位?”

目录

说说你对AQS的理解?

 你知道AQS的原理是什么吗?

AQS对资源的共享模式有哪些?

AQS中有哪些需要重写的方法?

使用线程池有哪些好处?⭐

创建线程池的参数有哪些?⭐

线程池中线程数一般怎么设置?需要考虑哪些因素?

如何创建线程池?⭐

用于提交任务的execute()和submit()方法有什么区别?


话不多说,发车!

说说你对AQS的理解?

全称是AbstractQueuedSynchronizer,队列同步器,是用来构建锁或者同步器的基础框架

Java实习生------JUC并发编程(多线程)10道面试题打卡(AQS队列同步器、线程池)⭐⭐⭐_第1张图片

 你知道AQS的原理是什么吗?

AQS核心思想是,如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并且将共享资源设置为锁定状态。

Java实习生------JUC并发编程(多线程)10道面试题打卡(AQS队列同步器、线程池)⭐⭐⭐_第2张图片

 

 如果被请求的共享资源被占用,那么就需要一套线程阻塞等待以及被唤醒时锁分配的机制,这个机制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方法会阻塞当前线程直到任务完成

Java实习生------JUC并发编程(多线程)10道面试题打卡(AQS队列同步器、线程池)⭐⭐⭐_第3张图片

整理面经不易,觉得有帮助的小伙伴点个赞吧~感谢收看! 

 

你可能感兴趣的:(多线程,java,面试)