【多线程初阶五】线程池&&常考面试题

目录

🌟一、线程池

🌈1、线程池是什么?

🌈2、为什么要使用线程池?

🌈3、怎么使用线程池?

        1、使用标准库中的线程池(6种)——>不推荐使用。  

        2、自定义一个线程池(重点)


🌟一、线程池

🌈1、线程池是什么?

        简单理解就是:在线程池中已经存在了一些创建好的线程,只需要往线程池中提交任务即可,当任务被提交到线程池之后,任务就会被自动执行。当程序启动的时候,如果发现有任务就立刻执行,没有任务就阻塞等待。

🌈2、为什么要使用线程池?

        线程池最大的好处就是减少频繁创建和销毁线程的系统的开销,从而提高效率。


🌈3、怎么使用线程池?

        1、使用标准库中的线程池(6种)——>不推荐使用。  

        (1)6种方法     

public static void main(String[] args) {
        // 1. 用来处理大量短时间工作任务的线程池,如果池中没有可用的线程将创建新的线程,如果线程空闲60秒将收回并移出缓存
        ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
        // 2. 创建一个操作无界队列且固定大小线程池
        ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
        // 3. 创建一个操作无界队列且只有一个工作线程的线程池
        ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
        // 4. 创建一个单线程执行器,可以在给定时间后执行或定期执行。
        ScheduledExecutorService singleThreadScheduledExecutor = Executors.newSingleThreadScheduledExecutor();
        // 5. 创建一个指定大小的线程池,可以在给定时间后执行或定期执行。
        ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(3);
        // 6. 创建一个指定大小(不传入参数,为当前机器CPU核心数)的线程池,并行地处理任务,不保证处理顺序
        Executors.newWorkStealingPool();
    }

  ❓面试题1:如何自定义线程池?  创建线程池时构造方法的参数及含义?(重点)

        (1)  通过工厂方法获取的线程池,最终都是ThreadPoolExecutor对象。通过创建(new)一个 ThreadPoolExecutor的对象自定义线程池。

        (2)参数含义

 1️⃣corePoolSize:核心线程数,创建线程池时包含的最小线程数量。(一次性全部创建完成)

 2️⃣maxminPoolSize:最大线程数。当线程不够用时,允许系统可以创建的最多线程数(最大线程数-核心线程数)。

 3️⃣keepAliveTime:临时线程空闲的时长。

 4️⃣Timeunit unit:空闲的时间单位和keepAliveTime一起使用。

 5️⃣BlockingQueue:存放任务的阻塞队列。

 6️⃣threadfactory:线程工厂,规定了怎么去创建线程。用系统默认的就行。

 7️⃣RejectedExecutionHander hander:拒绝策略,触发时机:当线程池处理不了过多的任务时。


理解上述过程:

🌰1:吃火锅~

(1)火锅店一共有10张桌子(对应核心线程数);

(2)中午2.00去,店里没有人,随便坐,此时店里的桌子足够招待客人;(对应:当任务量较小时,核心线程完全可以处理)

(3)到了下午5/6点,到饭点了,来吃饭的人逐渐增多,此时10张桌子就已经坐满了(对应:核心线程数已满);

(4)再来的顾客就要排号(对应:往阻塞队列里面添加任务)

(5)当顾客人数越来越多,老板就在店门口加了5张桌子(对应:创建的临时线程数)

(6)当顾客慢慢吃完了,也没有新来的排号的顾客时,门口的5张桌子就空闲下来了,说明店里的10张桌子完全可以处理顾客的人数;

(7)当老板等待30min后(对应:线程的空间时间和时间单位)仍然没有新来的顾客,门口的桌子用不上了,那就可以收回了(对应:回收临时线程)

(8)当在用餐高峰期,门口的桌子也用完了,那对于新来的顾客执行拒绝策略。


 上述过程总结:

 

❓面试题2:描述一下线程池的工作原理?(上述7个参数是如何搭配使用的?)(重点)

(1)当任务添加到线程池中,先判断当前任务数是否大于核心线程数;

(2)如果任务数小于等于核心线程数,则直接执行任务,否则加入阻塞队列中等待;

(3)当阻塞队列满了之后,按照指定的最大线程数创建临时线程(最大线程数-核心线程数);

(4)当阻塞队列满了之后,而且临时线程也已经创建完成,再次提交任务的时候,就会执行拒绝策略

(5)当任务量减少且核心线程数完全够执行,临时线程达到一定的空间时长之后就会被回收。

理解拒绝策略:

❓问题3:为什么不推荐使用系统自带的线程池?

❓ 面试题3:创建线程池的时候,指定的核心线程数一般是多少比较合适?

(1)这个没有一个准确的答案,要根据业务场景和计算机配置来决定;

(2)对于计算密集型的程序,那么线程数可以适当的增大;对于IO密集型的程序,取决于磁盘的读写效率,线程数过大也不会提高程序的效率;

(3)还需要考虑COU的核心数量是多少;

(4)最终要通过测试对比,来确定一个合适的线程数。

(2)演示使用JDK提供的方法实现线程池

(1)创建线程

(2)提交线程

        threadPool.submit();

//模拟实现线程池:一共有10个任务,线程池有3个线程。将任务提交到线程池,期望输出结果:每次这10个线程都是由线程池中的三个线程执行的。
    public static void main(String[] args) throws InterruptedException {
        //1、创建一个大小为3的线程池
        ExecutorService threadPool = Executors.newFixedThreadPool(3);
        //2、提交任务到线程池
        for (int i = 0; i < 10; i++) {
            int taskId = i;
            //3、表示要执行的任务
            threadPool.submit(()->{
                System.out.println("正在执行任务"+taskId+","+Thread.currentThread().getName());
            });
        }
        //4、等待任务执行
        TimeUnit.SECONDS.sleep(5);
        System.out.println("任务执行结束");
    }


  2、自定义一个线程池(重点)

🍀 创建一个线程池需要满足的条件:

        (1)需要提交任务到线程池,那么就要有一种数据结构来保存我们提交的任务;(考虑用阻塞队列实现)

        (2)创建线程时需要指定初始线程数量,这些线程不停的扫描阻塞队列,一旦有任务就立刻执行。(可以考虑用线程池对象的构造方法,接收要创建线程的数据,并在构造方法中完成线程的创建)   

自定义实现线程池: 

public class a03_MyThreadPool {
    //目标:用阻塞队列模拟线程池(创建一些线程表示线程池中已有的工作线程),往线程池中提交任务后,任务被工作线程执行
    //1、定义一个阻塞队列(就代表线程池),初始化容量为3。--------不理解。注意类型为Runnable
    BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>(3);
    //2、核心方法:对外提供一个方法,用来往线程池中提交任务
    public void submit(Runnable task) throws InterruptedException {
        queue.put(task);
    }
    //3、构造方法中,就要创建一些工作线程,让这些工作俩执行上述提交的任务
    public a03_MyThreadPool(int capacity){
        if(capacity <= 0){
            throw new RuntimeException("线程池的数量不能小于0");
        }
        //4、在线程池中创建一些工作线程(创建的个数就是初始化线程池给定的容量数)
        for (int i = 0; i < capacity; i++) {
            Thread thread= new Thread(()->{
                //5、不停的扫描
                while (true){
                    try {
                        //6、取出提交的任务,并执行
                        Runnable task = queue.take();
                        task.run();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
            //7、启动线程
            thread.start();
        }
    }
}

测试类:

public static void main(String[] args) throws InterruptedException {
        //1、创建一个大小为3的线程池
        a03_MyThreadPool threadPool = new a03_MyThreadPool(3);
        //2、提交任务到线程池
        for (int i = 0; i < 10; i++) {
            int taskId = i;
            //3、表示要执行的任务
            threadPool.submit(()->{
                System.out.println("正在执行任务"+taskId+","+Thread.currentThread().getName());
            });
        }
        //4、等待任务执行
        TimeUnit.SECONDS.sleep(5);
        System.out.println("任务执行结束");
    }

执行结果:


不要焦虑,行动解决焦虑!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值