• 实现线程池


    1.线程池是什么?

    线程池,字面意思来说,有点类似与我们的字符串常量池,数据库连接池等等。

    1.1 为啥要引入线程池这一概念呢??

    🍃这得从引入线程说起,,

    🍃进程本身已经能做到 并发编程了,为啥还要有线程??进程太重量了,创建和销毁成本都比较高(需要申请、释放资源),而线程就是针对上述问题的优化(同一个进程中的线程共用同一组系统资源)

    🍃虽然如此,但是在更频繁的创建、释放资源的场景下,线程也有点扛不住了!!

    所以,进一步优化,就引入了线程池。线程池存在的目的,就是为了让程序猿不必创建新的线程,直接使用已有的线程完成想要进行的工作即可!!

    🍁【线程池解决问题的思路】

    把线程创建好了之后,放到池子里,需要使用线程,就直接从池子里取,而不是通过系统来创建。当线程用完了,又放回池子里,而不是通过系统来销毁!!

    1.2 为什么把线程放到池子里,从池子里取线程就要比从系统这里创建线程效率高呢??

    🍃1.从池子里取,是纯用户态操作。

    🍃2.通过系统来创建,涉及到内核操作。

    而我们通常认为,牵扯到内核态操作,就要比纯用户态的操作更低效!!结合下图场景理解

     


     2.标准库中的线程池

    🍃1.使用 Executors.newFixedThreadPool(10) 能创建出固定包含 10 个线程的线程池.

    🍃2.返回值类型为 ExecutorService
    🍃3.通过 ExecutorService.submit 可以注册一个任务到线程池中 .
    1. public static void main(String[] args) {
    2. ExecutorService threadPool = Executors.newFixedThreadPool(10);
    3. for(int i = 0; i < 100; i++) {
    4. threadPool.submit(new Runnable() {
    5. @Override
    6. public void run() {
    7. System.out.println("hello");
    8. }
    9. });
    10. }
    11. }

    注意这行代码:

    ExecutorService threadPool = Executors.newFixedThreadPool(10);

    newFixedThreadPool Executors 类的一个静态方法,像这种借助静态方法创建实例,这样的方法,称为"工厂方法",对应的设计模式,就叫做"工厂模式"。

    通常情况下,创建对象,是借助 new ,调用构造方法来实现的。但是 Java 里面的构造方法,有诸多限制,在很多时候不方便使用,因此就需要给构造方法再包装一层,外层起到包装作用的方法就是工厂方法!!

    🍃为什么构造方法有时候不方便使用??

    构造方法的限制在于,当前构造方法的名字务必是和类名一样。要想实现不同版本的构造,就得通过重载,但是重载又要求参数类型和个数不同。当我们的场景刚好是参数类型相同且参数个数相同的时候,重载就行不通了!!请看以下例子:

    当我需要用构造方表示两个点:一个是笛卡尔坐标系,构造点;一个是极坐标系构造点。

    1. class Point {
    2. // 笛卡尔坐标系,构造点
    3. public Point(double x, double y) { };
    4. // 使用极坐标系,构造点
    5. public Point(double r, double a) { };
    6. // Error : 编译错误
    7. }

    显然,这段代码编译错误,不符合重载规则,这时候我们就需要使用工厂模式来解决上述问题了。

    1. class Point {
    2. // 笛卡尔坐标系,构造点
    3. public static Point makePointByXY(double x, double y) {
    4. Point point = new Point();
    5. point.setX(x);
    6. point.setY(y);
    7. return point;
    8. }
    9. // 极坐标系,构造点
    10. public static Point makePointByRA(double r, double a) {
    11. Point point = new Point();
    12. point.setR(r);
    13. point.setA(a);
    14. return point;
    15. }
    16. }

    于是此时实例化只需要通过类名调用需要的静态方法即可创建!!

    🍁Executors 创建线程池的几种方式:

    🍃1.newFixedThreadPool: 创建固定线程数的线程池
    🍃2.newCachedThreadPool: 创建线程数目动态增长的线程池 .
    🍃3.newSingleThreadExecutor: 创建只包含单个线程的线程池 .
    🍃4.newScheduledThreadPool: 设定 延迟时间后执行命令,或者定期执行命令 . 是进阶版的 Timer.

     Executors 里面的各种工厂方法,其实都是针对 ThreadPoolExecutor 这个类进行了 new 并且闯入不同风格的参数,来达到构造不同种类线程池的目标!!


    3.实现线程池

    1. class MyThreadPool {
    2. // 这个队列就是 "任务队列" 把当前线程要完成的任务都放入到这个队列中
    3. // 再由线程池内部的工作线程负责完成它们!!
    4. private BlockingQueue queue = new LinkedBlockingQueue<>();
    5. // 核心方法,往线程池里插入任务
    6. public void submit(Runnable runnable) {
    7. try {
    8. queue.put(runnable);
    9. } catch (InterruptedException e) {
    10. e.printStackTrace();
    11. }
    12. }
    13. // n : 设定线程池里有几个线程
    14. public MyThreadPool(int n) {
    15. // 构造方法中就需要创建一些线程,让这些线程负责执行上述插入的任务
    16. for(int i = 0; i < n; i++) {
    17. Thread t = new Thread(() -> {
    18. while(!Thread.currentThread().isInterrupted()) {
    19. try {
    20. // 取出任务,直接执行
    21. Runnable runnable = queue.take();
    22. runnable.run();
    23. } catch (InterruptedException e) {
    24. e.printStackTrace();
    25. }
    26. }
    27. });
    28. t.start();
    29. }
    30. }
    31. }
    32. public class TestDemo3 {
    33. public static void main(String[] args) {
    34. // 10 个线程一起执行 100 个任务
    35. MyThreadPool myThreadPool = new MyThreadPool(10);
    36. for(int i = 0; i < 100; i++) {
    37. myThreadPool.submit(new Runnable() {
    38. @Override
    39. public void run() {
    40. System.out.println("hello");
    41. }
    42. });
    43. }
    44. }
    45. }

    线程池的实现相比于定时器,要简单太多了!! 

    🍁【基本步骤】

    🍃1.核心操作为 submit,将任务加入线程池中;

    🍃2.使用一个 BlockingQueue 组织所有的任务;

    🍃3.每个线程,不停的从 BlockingQueue 中取任务并执行即可。


    本篇博客就到这里了,谢谢观看!!

  • 相关阅读:
    PyTorch、TensorFlow和Jax构建神经网络模型的标准化流程
    Awake()、OnEnable()、Start()、Reset()、OnDisable()、OnDestroy()、OnValidate()
    部门来了个测试开发,听说是00后,上来一顿操作给我看呆了...
    Kotlin协程:父子协程的绑定与传递
    如何备份 WordPress 数据库
    2.1、物理层的基本概念
    MySql学习笔记:索引和索引创建策略
    微信小程序实现腾讯地图
    增强大型语言模型(LLM)可访问性:深入探究在单块AMD GPU上通过QLoRA微调Llama 2的过程
    EM算法公式推导
  • 原文地址:https://blog.csdn.net/xaiobit_hl/article/details/126061993