当前位置: 首页 > article >正文

多线程 (九) 线程池的使用及实现

🎉🎉🎉点进来你就是我的人了
博主主页:🙈🙈🙈戳一戳,欢迎大佬指点!

人生格言:当你的才华撑不起你的野心的时候,你就应该静下心来学习!

欢迎志同道合的朋友一起加油喔🦾🦾🦾
目标梦想:进大厂,立志成为一个牛掰的Java程序猿,虽然现在还是一个🐒嘿嘿
谢谢你这么帅气美丽还给我点赞!比个心


目录

 一.线程的概念

二、创建线程池的方式

三.线程池的创建(工厂模式创建)

四.线程池工作原理及参数

五. 四种拒绝策略

六.自定义一个线程池



 一.线程的概念

线程池:一个容纳多个线程的容器,容器中的线程可以重复使用,省去了频繁创建和销毁线程对象的操作。

线程池作用:

  • 降低资源消耗,减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务。
  • 提高响应速度,当任务到达时,如果有线程可以直接用,不会出现系统僵死。
  • 提高线程的可管理性,如果无限制的创建线程,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。

线程池的核心思想:线程复用,同一个线程可以被重复使用,来处理多个任务。

池化技术 (Pool) :一种编程技巧,核心思想是资源复用,在请求量大时能优化应用性能,降低系统频繁建连的资源开销。

为什么从线程池取线程要比从系统申请效率更高呢

因为从线程池取线程是纯粹的用户态操作

从系统创建线程,涉及到内核态和用户态的切换

内核态用户态是什么?

操作系统对程序的执行权限进行分级,分别为用户态和内核态。用户态相比内核态有较低的执行权限,很多操作是不被操作系统允许的,简单来说就是用户态只能访问内存,防止程序错误影响到其他程序,而内核态则是可以操作系统的程序和普通用户程序

内核态: cpu可以访问计算机所有的软硬件资源

用户态: cpu权限受限,只能访问到自己内存中的数据,无法访问其他资源

为什么要有用户态和内核态?
系统需要限制不同的程序之间的访问能力,防止程序获取不相同程序的内存数据,或者外围设备的数据,并发送到网络,所有cpu划分出两个权限等级用户态和内核态

举个简单的例子加深理解

一位滑稽老铁去银行取钱,如果自己操作ATM机取钱效率很高,如果让工作人员去帮忙取钱,此时他可能在取钱的过程中先接杯水,再和别人唠会嗑啥的,效率会很低!

这里的滑稽老铁就是用户态,工作人员就是内核态,滑稽老铁自己取钱相当于线程池取线程,是纯用户态的操作,而交给工作人员去取钱相当于向系统申请资源创建线程,涉及到到内核态和用户态的切换

二、创建线程池的方式

线程池的创建⽅式总共包含以下 7 种(其中 6 种是通过 Executors 创建的, 1 种是通过

ThreadPoolExecutor 创建的):

        1. Executors.newFixedThreadPool:创建⼀个固定⼤⼩的线程池,可控制并发的线程数,超出的线程会在队列中等待;

        2. Executors.newCachedThreadPool:创建⼀个可缓存的线程池,若线程数超过处理所需,缓存⼀段时间后会回收,若线程数不够,则新建线程;

        3. Executors.newSingleThreadExecutor:创建单个线程数的线程池,它可以保证先进先出的执⾏顺序;

        4. Executors.newScheduledThreadPool:创建⼀个可以执⾏延迟任务的线程池;

        5. Executors.newSingleThreadScheduledExecutor:创建⼀个单线程的可以执⾏延迟任务的线程池;

        6. Executors.newWorkStealingPool:创建⼀个抢占式执⾏的线程池(任务执⾏顺序不确定)【JDK1.8 添加】。

        7. ThreadPoolExecutor:最原始的创建线程池的⽅式,它包含了 7 个参数可供设置,后⾯会详细讲。

三.线程池的创建(工厂模式创建)

 工厂模式实际上就是拿来填构造方法的坑的

例如当我们算一个坐标的时候,我们可以通过横纵坐标构造一个点,也可以通过极坐标的方式构造一个点,但是在构造的时候我们发现两种的参数是一样的,无法构成重载

于是就可以利用工厂模式构建一个工厂类,搞两个普通的方法重载去实现,问题就能得到解决

伪代码演示:

//构造方法方法冲突
class  Point{
public Point(double x,double y)

}

public  Point(double r,double a){
}

}

//创建一个工厂类
class  PointBuilder{
public static point getPointA(double x,double y)

}

public  static point getPointB(double r,double a){
}

}

四.线程池工作原理及参数

 Executors类提供4个静态工厂方法:newCachedThreadPool()、newFixedThreadPool(int)、newSingleThreadExecutor和newScheduledThreadPool(int)。这些方法最终都是通过ThreadPoolExecutor类来完成的,这里强烈建议大家直接使用Executors类提供的便捷的工厂方法,能完成绝大多数的用户场景,当需要更细节地调整配置,需要先了解每一项参数的意义。

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue) {
    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
         Executors.defaultThreadFactory(), defaultHandler);
}
  1. corePoolSize(线程池基本大小)必须大于或等于0;
  2. maximumPoolSize(线程池最大大小)必须大于或等于1;
  3. maximumPoolSize必须大于或等于corePoolSize;
  4. keepAliveTime(线程存活保持时间)必须大于或等于0;
  5. workQueue(任务队列)不能为空;
  6. threadFactory(线程工厂)不能为空,默认为DefaultThreadFactory类
  7. handler(线程饱和策略)不能为空,默认策略为ThreadPoolExecutor.AbortPolicy。

五. 四种拒绝策略

CallerRunsPolicy呼叫着运行策略,通常叫做,调用者运行策略:是如果线程池的线程全部被用完的时候,会把多余的任务返回给调用者去执行;(敢于反驳,我干不了了,就把任务丢给发布任务的人去干,哈哈哈,打工人要学学,不能一味的屈服)
AbortPolicy终止策略:如果线程池线程被用完了,直接抛出异常 rejectedExecution从而终止任务;
DiscardPolicy丢弃策略:如果线程池的线程被用完了,不抛出异常,直接丢弃多余的任务;(很有脾气是吧,我干不了我不干,我也不吭气,直接仍了,哈哈哈)
DiscardOldestPolicy丢弃最老策略:如果线程池的线程被用完了,就把等待时间最久的任务丢弃掉;(这是不是工作中,把一些棘手的任务放到最后,一直到发布任务的人忘记了有这么一回事了,然后我们也可以不干了,哈哈哈哈)

六.自定义一个线程池

//自定义一个线程池
class MyThreadPool {
    //阻塞队列用来存放任务
    private BlockingQueue<Runnable> queue =new LinkedBlockingDeque<>();

    public void submit(Runnable runnable) throws InterruptedException {
        queue.put(runnable);
    }
    //此处实现一个固定线程数的线程池
    public MyThreadPool(int n) {
        for (int i = 0; i < n; i++) {
            Thread t =new Thread(() -> {
                    try {
                        while (true) {
                            //取任务
                            Runnable runnable = queue.take();
                            runnable.run();
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
            });
            t.start();
        }
    }
}
public class ThreadDemo1 {
    public static void main(String[] args) throws InterruptedException {
        MyThreadPool myThreadPool =new MyThreadPool(10);
        for (int i = 0; i < 1000; i++) {
            int number =i;
            myThreadPool.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println("hello "+number);
                }
            });
        }
    }
}


http://www.kler.cn/a/3169.html

相关文章:

  • 【性能优化专题系列】利用CompletableFuture优化多接口调用场景下的性能
  • Hive:Hive Shell技巧
  • 如何看待 OpenAI 的12天“shipmas”发布计划?
  • OpenCV:形态学操作总结
  • 【xcode 16.2】升级xcode后mac端flutter版的sentry报错
  • 深入探讨数据库索引类型:B-tree、Hash、GIN与GiST的对比与应用
  • 若依框架---权限管理设计
  • C++三种继承方式
  • Github学生包申请秒过经验并使用Copilot
  • 【linux】进程信号——信号的保存和处理
  • 样本量很少如何获得最佳的效果?最新小样本学习工具包来啦!
  • 基于Golang实现多人在线游戏的AOI算法
  • java如何手动导jar包
  • 78.qt QCustomPlot介绍
  • HTTP/2.x:最新的网页加载技术,快速提高您的SEO排名
  • 如何在 Vue 中使用 防抖 和 节流
  • Flutter GetX 实现 ChatGPT 简单聊天界面
  • 学大数据算跟风吗?
  • 八大排序算法之归并排序(递归实现+非递归实现)
  • 全景丨0基础学习VR全景制作,后期篇第九章:控制点和遮罩工具
  • 蓝桥杯Web前端练习题-----水果拼盘
  • acm省赛:高桥和低桥(三种做法:区间计数、树状数组、线段树)
  • chatGPT爆火,什么时候中国能有自己的“ChatGPT“
  • PID控制算法详解
  • 刷题之最长公共/上升子序列问题
  • Android实时显示时间日期(极简)