【十】一文带你迅速掌握线程池

线程池是预先创建的线程集合,减少频繁创建销毁的开销,提高效率。Java标准库提供了ExecutorService,如newFixedThreadPool,通过ThreadPoolExecutor管理核心线程数、最大线程数、任务队列和拒绝策略。文章还介绍了四种线程池的拒绝策略,并展示了自定义线程池的实现示例,强调线程数量应根据任务类型(CPU密集型或IO密集型)和测试调整。
摘要由CSDN通过智能技术生成

1. 什么是线程池

所谓的线程池就是提前把线程准备好,创建线程不是直接从系统申请,而是从池子里拿~
线程不用了,就还给池子~
池的目的是为了提高效率,线程的创建,虽然比进程轻量,但是在频繁创建的情况下,开销也是不可忽略的!
那么问题来了,为什么在池子里面拿线程就要比系统创建线程更高效呢?

  • 从池子拿线程,纯粹的用户态操作
  • 从系统创建线程,涉及到用户态和内核态之间的切换,真正的创建是要在内核态完成的~

下面简单介绍下用户态和内核态:

一个操作系统 = 内核 + 配套的应用程序
所谓的内核,就是操作系统最核心的功能模块集合,硬件管理、各种驱动、进程管理、内存管理、文件系统…
而内核就需要给应用层序提供给支持~

举个最简单的例子:
比如 print(“hello world”),应用程序就需要调用系统内核,告诉内核,要进行一个打印操作,内核再通过驱动程序,操作显示器,完成上述功能~
应用程序同一时刻,可以有很多个,而内核只有一个,所有有些时候,处理并不能那么及时~

总结:纯用户态操作时间是可控的,涉及到内核态操作,时间是不可控的

2. 标准库中的线程池

首先简单介绍下工厂模式:
所谓的工厂模式,就是用来填补构造方法的坑,我们都知道如果要提供多种构造对象的方式,就得基于重载!
可以举个例子:描述平面中点,可以用直角坐标系来表示,也可以用极坐标系来表示

// 此处写的伪代码
class Point{
	public Point(double x ,double y){
		//....	
	}
	public Point(double r, doubule a){
		//....
	}
}

上述代码肯定是不可以的!!无法构成重载,此时就需要构造一个工厂类

class PointBuilder{
	public static Point makePointByXY(double x,double y){
		//....
	}
	public static Point makePointByRA(double r,double a){
		//...
	}
}

这样问题就迎刃而解了!这就是工程模式!


Java中标准库提供的线程池使用

public class Demo26 {
    public static void main(String[] args) {
        ExecutorService pool = Executors.newFixedThreadPool(10); // 固定10个
        // ExecutorService pool = Executors.newCachedThreadPool(); 按需创建
        pool.submit(new Runnable() {
            @Override
            public void run() {
                System.out.println("hello");
            }
        });
    }
}

下面我们来分析下这段代码:
在这里插入图片描述

3. ThreadPoolExecutor类

可以点击 JAVA官方文档 查看该类的介绍
在这里插入图片描述

  • int corePoolSize :核心线程数
  • int maximumPoolSize:最大线程数
  • long keepAliveTime:临时线程存活的时间
  • TimeUnit unit:存活时间的单位
  • BlockingQueue <Runnable > workQueue : 阻塞队列,管理任务
  • ThreadFactory threadFactory:线程工厂,创建线程的辅助的类
  • RejectedExecutionHandler handler:线程池的拒绝策略,当线程池的任务缓存队列已满并且线程池中的线程数目达到maximumPoolSize时,如果还有任务到来就会采取任务拒绝策略

3.1 标准库提供的四种拒绝策略(重点掌握)

在这里插入图片描述

  • 第一个 ThreadPoolExecutor.AbortPolicy : 如果满了,继续添加任务,添加操作直接抛出异常
  • 第二个 ThreadPoolExecutor.CallerRunsPolicy:添加的线程自己执行任务
  • 第三个 ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新提交被拒绝的任务
  • 第四个 ThreadPoolExecutor.DiscardPolicy:丢弃任务,不抛异常

4. 自己实现一个线程池

class MyThreadPool{
    // 阻塞队列用来存任务
    private BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>();
    public void submit(Runnable runnable) throws InterruptedException {
        queue.put(runnable);
    }

    // 此处实现一个固定线程数的线程池
    public MyThreadPool(int n){
        for (int i = 0; i < 10; i++) {
            Thread t = new Thread(()->{
                while (true){
                    // 此处需要让线程内部有个 while 循环,不停的取任务
                    try {
                        Runnable runnable = queue.take();
                        runnable.run();
                        Thread.sleep(1000);

                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            });
            t.start();
        }
    }
}
public class Demo20 {
    public static void main(String[] args) throws InterruptedException {
        MyThreadPool pool = new MyThreadPool(10);
        for (int i = 0; i < 1000; i++) {
            int number = i;
            pool.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println("hello " + number);
                }
            });
        }
    }
}

在这里插入图片描述
从结果可以看出,线程池中任务执行的顺序和添加顺序不一定相同的!非常正常,这十个线程是无序调度的~

在上述代码中有两个小知识点:
在这里插入图片描述


在当前代码中,搞了10个线程的线程池,在实际开发中,一个线程池的线程数量,设置成多少,才合适呢?

不同的程序,线程做的活不一样

  1. CPU 密集型任务,做一些计算工作,要在CPU上运行
  2. IO密集型,主要是等待IO操作(等待读写硬盘,读写网卡),这些不咋吃CPU

所以极端情况下,如果你的线程全是使用CPU,线程数就不应该超过CPU核心数
如果你的线程全是IO操作,那线程数可以设置很多,可以远远超过CPU核心数

实践中,还是需要通过测试来确定~ 取一个执行效率OK,并且占用资源也OK的数量~

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

一个想打拳的程序员

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

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

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

打赏作者

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

抵扣说明:

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

余额充值