[07]从零开始的JAVAEE-线程池

线程池通过预先创建线程提高效率,避免频繁创建销毁的开销。内核态和用户态影响线程创建效率,线程池从池中获取线程是用户态操作,更高效。Java中使用ExecutorService创建线程池,如newFixedThreadPool,提供固定数量的工作线程。线程池包含核心线程数、最大线程数、存活时间和阻塞队列等概念,还有不同的拒绝策略处理任务过剩情况。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 目录

一、什么是线程池

为什么从线程池拿线程比创建线程更为高效?

内核态

用户态

二、线程池的使用

线程池的构造方法

代码模拟实现线程池

分析

实现


一、什么是线程池

线程的创建,虽然相比于进程更为轻量,但在频繁创建的情况下,开销也是不可忽略的,为了提高效率,可以引入线程池,提前将线程创建好,放入池子中,后续需要用到线程时,直接从线程池中申请,用完以后还回去即可,这样就免去了一直创建销毁线程,提高了效率

为什么从线程池拿线程比创建线程更为高效?

为了理解这个问题,我们需要理解操作系统中的两个基本概念:用户态和内核态

一个操作系统 = 内核 + 配套的应用程序

内核:操作系统最核心的功能模块集合,例如硬件管理、各种驱动、进程管理......且内核需要给上层应用程序提供支持,例如打印一个hello,应用程序就需要调用内核,然后内核在通过操作系统调用显示器完成打印

应用程序在同一时刻会有许多,但内核只有一个,内核同时给多个应用程序提供服务,有时就会导致服务不那么及时

以一个银行取钱的例子做演示

 在这个例子中,柜台的服务员就相当于一个内核,来取钱的人就相当于应用程序

内核态

内核态就是服务员的操作,它的时间不可控,因为服务员也有可能在给你取钱的过程中上个厕所什么的

用户态

时间可控,用户可以自行决定时间

系统创建线程,就是一个内核态的操作,系统从线程池中拿线程,就是用户态的操作,这就是为什么线程池比创建线程更高效的原因

二、线程池的使用

标准库中提供了线程的线程池

ExecutorService pool = Executors.newFixedThreadPool(10);//创建一个有10个线程的线程池

//添加任务到线程池中
pool.submit(new Runnable(){
        @Override
        public void run(){
            System.out.println("hello");
        }
    });

这里需要注意的是,此处线程池的创建并非直接new的对象,而是通过Executors类里面的静态方法完成的对象构造,这是典型的工厂模式,工厂模式会在后面详细介绍。

 Executors.newFixedThreadPool(10)这个类实际上是对ThreadPoolExecutor这个类进行进一步封装实现的,ThreadPoolExecutor是原装的线程池

线程池的构造方法

线程池的构造方法有很多,查阅官方文档可以查到这些

这里主要介绍最后一条构造方法

ThreadPoolExecutor(
int corePoolSize, 
int maximumPoolSize, 
long keepAliveTime, 
TimeUnit unit, 
BlockingQueue<Runnable> workQueue, 
ThreadFactory threadFactory, 
RejectedExecutionHandler handler
);
  •  int corePoolSize:核心线程数
  • int maximumPoolSize:最大线程数

如果把线程池比作公司,核心线程数就是其中的正式员工,最大线程数就是正式员工+实习生。我们知道,正式员工需要与公司签订劳动合同,不能随便辞退,而实习生不签订,可以随便辞退,万恶的资本家就是这样剥削我们的,当公司里比较忙时,正式员工+实习生一起上,当比较清闲时,就把实习生给辞了

  • long keepAliveTime:存活时间
  • TimeUnit unit:存活时间单位

当公司内较为清闲时,公司也无法预知在接下来的时间里会不会突然忙起来,所以不会立马把实习生都辞退了,它们会先让实习生待一段时间,如果一段时间过后,公司仍然清闲,说明接下来的一段时间内大概率是不会忙了,这段时间过后再把实习生给裁掉,这里等待的一段时间就是存活时间,存活时间单位是毫秒、秒、分钟这些单位

  • BlockingQueue<Runnable> workQueue:阻塞队列

线程池中要管理很多任务,所以我们可以手动给线程池添加一个阻塞队列,来方便控制、获取队列中的信息,submit方法就是把任务放到队列中

  • ThreadFactory threadFactory:线程工厂

工厂模式,就是创建线程的辅助类,无需做过多了解,知道即可

  • RejectedExecutionHandler handler:拒绝策略

当线程池中池子满了,再往其中添加任务,应该如何拒绝,下图是标准库中的四种拒绝策略 

  • 如果满了,继续添加任务,直接抛出异常:例如你今天已经安排满了,你的上司还给你安排了任务,此时你绷不住了,哇的一声哭了出来
  • 谁添加,谁负责:你的上司给你安排任务之后,你回怼:我才不去,你自己安排的你自己去吧
  • 丢弃最老的任务:丢弃最后执行的安排,把上司安排的任务添加进去(阻塞队列的队首元素)
  • 丢弃最新任务:丢弃最先执行的安排,就是不管上司给你安排了什么,统统丢弃,还是该干嘛干嘛。

JAVA标准库创建线程池的四种方式

在java标准库中自带了四种创建线程池的方法

  • newFixedThreadPool(int n):创建一个固定大小的线程池,线程数量为 n 个,所有任务都会放入队列中等待执行。

   ExecutorService pool = Executors.newFixedThreadPool(10);//创建十个线程的线程池
  • newCachedThreadPool():创建一个可缓存的线程池,线程数量不定,根据需要动态创建线程,线程空闲超过 60 秒将被终止并回收。

ExecutorService pool = Executors.newCachedThreadPool();//创建缓存线程池,线程数量不固定,动态开辟
  • newSingleThreadExecutor():创建一个单一线程池,线程数量为 1。所有任务都会交给该线程依次执行。

  ExecutorService pool = Executors.newSingleThreadExecutor();//创建一个只有一个线程的线程池
  • newScheduledThreadPool(int n):创建一个定长线程池,线程数量为 n 个,可以按照指定的延迟时间或者固定间隔周期执行任务,功能强大。

 ExecutorService executor = Executors.newScheduledThreadPool(2);//定长线程池,可以按照指定延迟时间执行任务

代码模拟实现线程池

分析

一个简易完整的线程池,具有以下的属性

  • 阻塞队列:用于存放任务
  • submit方法,用于添加任务
  • 创建固定数量的线程池:用于创建线程

线程池中线程的数量并不是越多越好,在实际开发中,需要参考cpu的核心数量,假设cpu核心数是N,一般设置成N、N+1、2N、1.5N。等等,需要实际测试才能得出具体结论

实现

class MyThreadPool {
    private BlockingDeque<Runnable> queue = new LinkedBlockingDeque<>();//阻塞队列,存放任务

    /**
     * 用于添加任务到阻塞队列的方法
     * @param task:任务
     * @throws InterruptedException
     */
    public void submit(Runnable task) throws InterruptedException {
        queue.put(task);
    }

    /**
     * 构造方法,参数为int,表示创建多少个工作线程
     * @param n:线程的数量
     */
    public MyThreadPool(int n){
        for (int i = 0; i < n; i++) {
            Thread t = new Thread(()->{
                try {
                    //一直取任务 使用while循环,当队列中没有任务时不会创建新的线程,所以线程不是恒定不变的,n只是一个上限
                    while (true){
                        Runnable runnable = queue.take();//取出任务
                        runnable.run();//运行
                    }

                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
            t.start();//创建完就运行
        }
    }

}

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不卷啊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值