美团--并发编程面试题

并发编程

1、线程池参数,大概流程,拒绝策略是什么?
public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler)
参数:

corePoolSize 核心线程数

maximumPoolSize 最大线程数(应急线程数||空闲线程)

keepAliveTime 针对空闲线程的存活时间 如果超时了则把空闲的线程kill

unit 存活时间的单位

workQueue 任务存放的队列

threadFactory 线程工厂,主要是产生线程---给线程起个自定义名字

handler 拒绝策略

大概流程:

线程池一开始没有线程,任务提交过来,线程池会创建新的线程去执行任务;当线程数量大于核心线程数时,新加的任务会就加入到任务队列中;如果是有界队列的话,任务数量超过任务队列大小时,线程池会创建maximumPoolSize - corePoolSize 个空闲线程来执行任务;如果线程数到达 maximumPoolSize 仍然有新任务进来,这时会执行拒绝策略

工厂方法:
1、newFixedThreadPool
public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
}
ExecutorService executorService = Executors.newFixedThreadPool(10);
for (int i = 0; i < 10; i++) {
    executorService1.submit(() -> {
        System.out.println(String.format("工厂方法=   固定线程数, 线程名称=%20s", Thread.currentThread().getName()));
        try {
            Thread.sleep(1000L);
        } catch (InterruptedException e) {
            log.error(e.getMessage());
        }
    });
}

核心线程数 == 最大线程数 (无空闲线程)所以也无需空闲线程超时时间

任务队列为无界队列,可以放任意数量的任务,任务队列使用链表堵塞队列 new LinkedBlockingQueue<Runnable>()

适应于已知任务量已知,相对耗时的任务

2、newCachedThreadPool
public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
}
ExecutorService executorService2 = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
    executorService2.submit(() -> {
        System.out.println(String.format("工厂方法=   缓存线程池, 线程名称=%20s", Thread.currentThread().getName()));
        try {
            Thread.sleep(1000L);
        } catch (InterruptedException e) {
            log.error(e.getMessage());
        }
    });
}

核心线程数为 0,最大线程数为 Integer.MAX_VALUE (2^31 - 1),全部都是空闲线程,超时时间为 60 秒

任务队列使用同步队列 new SynchronousQueue<Runnable>())

一个可根据需要创建新线程的线程池,如果现有线程没有可用的,就创建一个新线程加入到线程池中;如果有线程已被使用完但还没有被摧毁,则可以复用该线程;如果线程池中没有空闲线程则不会使用任何资源。

这种线程池比较灵活,对于需要执行短期异步任务的程序而言,这种线程池通常可以提高程序性能。

3、newSingleThreadExecutor
public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>(),
                                threadFactory));
}
ExecutorService executorService3 = Executors.newSingleThreadExecutor();
for (int i = 0; i < 10; i++) {
    executorService3.submit(() -> {
        System.out.println(String.format("工厂方法=   单个线程, 线程名称=%20s", Thread.currentThread().getName()));
        try {
            Thread.sleep(1000L);
        } catch (InterruptedException e) {
            log.error(e.getMessage());
        }
    });
}

希望多个任务排队执行,线程数固定为 1,任务数多于 1 时,会放入无界队列排队,任务执行完毕,这唯一的线程
也不会被释放。

区别于自己创建一个单线程串行执行任务,如果任务执行失败而终止那么没有任何补救措施,而线程池还会新建一
个线程,保证池的正常工作。

Executors.newSingleThreadExecutor() 线程个数始终为1,不能修改 ,Executors.newFixedThreadPool(1) 初始时为1,以后还可以修改,对外暴露的是 ThreadPoolExecutor 对象,可以强转后调用 setCorePoolSize 等方法进行修改;

提交任务:
// 提交一个任务 无返回值
void execute(Runnable command);

// 提交任务有返回值
<T> Future<T> submit(Callable<T> task);
Future<?> submit(Runnable task);
<T> Future<T> submit(Runnable task, T result);

// 提交所有的任务
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException;
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException;

// 提交所有任务,哪个任务先执行完成,返回该任务返回值,并取消其他任务
终止任务
void shutdown();
调用 shutdown() 方法,线程池状态变为 SHUTDOWN,不接受新任务,但已提交的任务会执行完,不会阻塞调用线程的执行。
List<Runnable> shutdownNow();
调用 shutdownNow(),线程池状态变为 STOP,不接受新任务,会将队列中的任务返回,会调用 interrupt 方法中断正在执行的任务。
调用 shutdown后,调用线程并不会等待所有任务运行结束,可以利用此方法等待
boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException;
拒绝策略:

拒绝策略使用场景,当线程数量到达 maximumPoolSize 时,仍有新任务加进来,就执行拒绝策略。

拒绝策略有几种:

  1. AbortPolicy 处理任务遭到拒绝并抛出异常 RejectedExecutionException。这是 Java 默认拒绝策略,任务队列满并且线程池满的情况下会直接拒绝任务执行,并抛出异常;
  2. CallerRunsPolicy 使用调用执行器 executor 的线程来执行被拒绝的线程。此策略提供简单的反馈控制机制,可以减缓新任务的提交速度,这个策略是不想丢弃当前任务的,但是线程池中已经没有任务可用资源了,所以就使用执行 executor 的线程来执行当前任务,但是有可能造成当前线程阻塞,导致后面的业务也无法执行,需要根据具体业务场景来定;
  3. DiscardPolicy 处理任务直接丢弃,不抛出异常。和 AbortPolicy 本质上是一样的,只是不抛出异常
  4. DiscardOldestPolicy 如果执行程序尚未关闭,就把任务队列头部的任务丢弃,再重新执行该任务。如果再次失败则重复该过程。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值