栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

多线程--02--01--线程池常见面试题

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

多线程--02--01--线程池常见面试题

一、线程池常见面试题 1.1、线程池参数(7-4-4)

1、corePoolSize:线程池的基本大小,当提交一个任务到线程池时,线程池会创建一个线程来执行任务,即使其他空闲的基本线程能够执行新任务也会创建线程,等到需要执行的任务数大于线程池基本大小时就不再创建。说白了就是,即便是线程池里没有任何任务,也会有corePoolSize个线程在候着等任务。

2、maximumPoolSize:最大线程数,不管你提交多少任务,线程池里最多工作线程数就是maximumPoolSize。

3、keepAliveTime:线程的存活时间。当线程池里的线程数大于corePoolSize时,如果等了keepAliveTime时长还没有任务可执行,则多出corePoolSize的线程退出。

4、unit:这个用来指定keepAliveTime的单位,比如秒:TimeUnit.SECONDS。

5、workQueue(4种):用于保存等待执行任务的阻塞队列,提交的任务将会被放到这个队列里。

ArrayBlockingQueue:是一个基于数组结构的有界阻塞队列,此队列按FIFO(先进先出)原则对元素进行排序。linkedBlockingQueue:是一个基于双向链表结构的阻塞队列,此队列按FIFO排序元素,吞吐量通常要高于ArrayBlockingQueue。静态工厂方法Executors.newFixedThreadPool()使用了这个队列。SynchronousQueue:是一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于linked-BlockingQueue,静态工厂方法Executors.newCachedThreadPool使用了这个队列。PriorityBlockingQueue:一个具有优先级的无限阻塞队列。

6、threadFactory:线程工厂,用来创建线程。主要是为了给线程起名字,默认工厂的线程名字:pool-1-thread-3。

7、handler:拒绝策略(4种),即当线程和队列都已经满了的时候,应该采取什么样的策略来处理新提交的任务。默认策略是AbortPolicy(抛出异常),其他的策略还有:CallerRunsPolicy(只用调用者所在线程来运行任务)、DiscardOldestPolicy(丢弃队列里最近的一个任务,并执行当前任务)、DiscardPolicy(不处理,丢弃掉)

美团面试题:

1、阻塞队列是啥?

java.util.concurrent.BlockingQueue的特性是:当队列是空的时,从队列中获取或删除元素的操作将会被阻塞,或者当队列是满时,往队列里添加元素的操作会被阻塞。阻塞队列不接受空值,当你尝试向队列中添加空值的时候,它会抛出NullPointerException。

2、无界阻塞队列会有什么问题?

当线程在执行任务时需要调用远程服务,当调用远程服务异常时,就会导致线程处理每个任务都需要等待很长的时间;处理任务的速度慢,产生任务的速度快,而任务队列是没有边界的,就会导致队列变得越来越大,从而导致内存飙升,还可能导致OOM内存溢出。所以往往需要指定阻塞队列大小:

public linkedBlockingQueue(int capacity)
1.2、线程池运行原理

任务被提交到线程池,会先判断当前线程数量是否小于corePoolSize,如果小于则创建线程来执行提交的任务,否则将任务放入workQueue队列,如果workQueue满了,则判断当前线程数量是否小于maximumPoolSize,如果小于则创建线程执行任务,否则就会调用handler,以表示线程池拒绝接收任务。

1.3、线程池中线程设置为多少合适?

线程池中线程设置为多少合适呢?我们采用的计算公式如下:

 1、I/O耗时、CPU耗时、现有CPU利用率可以通过APM工具(SkyWalking、zipkin等)获取; 

2、如上计算公式,适合于上线后根据运行数据设置,那么初始上线时如何设置线程池中线程数量呢:

根据经验:

1、cpu密集型:线程数量我们设为2N;

2、IO密集型:线程数量我们设为N+1;

其中N为cpu内核数量,可通过如下代码获取:

int N_CPUS = Runtime.getRuntime().availableProcessors();

3、如何判断是 CPU 密集任务还是 IO 密集任务?

CPU 密集型简单理解就是利用 CPU 计算能力的任务,比如你在内存中对大量数据进行排序。但凡涉及到网络读取,文件读取这类都是 IO 密集型,这类任务的特点是 CPU 计算耗费时间相比于等待 IO 操作完成的时间来说很少,大部分时间都花在了等待 IO 操作完成上。(web应用为IO密集型) 二、定时线程池使用举例 2.1 示例1

创建一个定时任务,每隔10秒执行一次

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import com.fuping3.rocketmq.javabasic.JavaBasicApplication;
import lombok.extern.slf4j.Slf4j;
 
 
@RunWith(SpringRunner.class)
@SpringBootTest(classes = JavaBasicApplication.class)
@Slf4j
public class ScheduleThreadTest {
 
    @Test
    public void test1() throws InterruptedException {
        //创建包含一个定时线程的线程池
        ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
        
        scheduledExecutorService.scheduleAtFixedRate(()->{
            log.info("schedule run pre 10 second");
        },5,10, TimeUnit.SECONDS);
 
        //保持程序运行状态
        while(true){
            Thread.sleep(1000);
        }
 
    }
}

打印输出

2021-11-24 22:05:18.519  INFO 9692 --- [pool-2-thread-1] ScheduleThreadTest                       : schedule run pre 10 second
2021-11-24 22:05:28.517  INFO 9692 --- [pool-2-thread-1] ScheduleThreadTest                       : schedule run pre 10 second
2021-11-24 22:05:38.525  INFO 9692 --- [pool-2-thread-1] ScheduleThreadTest                       : schedule run pre 10 second
 
.
.
.

2.2 示例2

springboot中使用定时任务

@Component
@Slf4j
public class A{
 
    @Scheduled(cron = "0 5 0 * * ?")
    public void scheduleMethod() {
        ...
        doScheduleMethod();
        ...
    }
}
三、ExecutorService的使用后注意关闭

1、使用完ExecutorService之后应该关闭它,否则它里面的线程会一直处于运行状态,会阻止JVM关闭。

public class PoolTest {
    public static void main(String[] args) throws InterruptedException {
        ExecutorService excuterService= Executors.newFixedThreadPool(10);
        for (int i=0;i<5;i++){
            System.out.println(i+"...");
            excuterService.submit(new TA());
        }
        //ExecutorService使用之后注意关闭
        excuterService.shutdown();
    }
 
}
 
class TA implements Runnable{
 
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"----");
    }
}

2、我们可以调用ExecutorService.shutdown()方法关闭ExecutorService。在调用shutdown()方法之后,ExecutorService不会立即关闭,但是它不再接收新的任务,直到当前所有线程执行完成才会关闭,所有在shutdown()执行之前提交的任务都会被执行。

如果我们想立即关闭ExecutorService,我们可以调用ExecutorService.shutdownNow()方法。这个动作将跳过所有正在执行的任务和被提交还没有执行的任务。但是它并不对正在执行的任务做任何保证,有可能它们都会停止,也有可能执行完成。

3、web应用程序中,如果ExecutorService是共享的,则不能关闭,不然需要重复创建线程池

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/781447.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号