• 多线程案例(线程池篇)


    线程池

    线程池是什么

    进程本身已经能够做到并发编程,但由于进程太"重量"了,创建和销毁的成本比较高(需要申请和释放资源),线程可以看作是轻量级的进程(共用一组系统资源)。虽然如此,但是在更频繁的创建和释放的情况下,线程也抗不住了,因此要做进一步的优化,其中有两种方式:线程池和协程(纤程)
    协程:轻量级线程

    线程池就是把线程创建好之后,放到池子里,需要使用线程,就直接从池子里取,而不是通过系统来创建,当线程用完了,就将线程还到池子里,而不是通过系统来销毁,通过线程池的方法来进一步提高效率

    【为什么将线程放到池子里,从池子里取线程就要比从系统创建线程更高效呢?】

    从池子里取线程,属于纯用户态操作,通过系统来创建线程,设及内核态操作,通常认为,牵扯到内核态的操作,就要比纯用户态的操作更低效

    【为什么用户态操作比内核态操作更高效呢?】

    在这里插入图片描述

    线程池存在的意义:减少用户态和内核态之间的交互,把更多的工作放到用户态去完成,通过这种方式提高程序效率
    协程:是一种轻量级的用户态线程。简单来说,线程的调度是由操作系统负责,线程的睡眠、等待、唤醒的时机是由操作系统控制,开发者无法决定。使用协程,开发者可以自行控制程序切换的时机,可以在一个函数执行到一半的时候中断执行,让出CPU,在需要的时候再回到中断点继续执行。因为切换的时机是由开发者来决定的,就可以结合业务的需求来实现一些高级的特性。

    标准库中的线程池

    • 使用 Executors.newFixedThreadPool(10) 能创建出固定包含 10 个线程的线程池.
    • 返回值类型为 ExecutorService
    • 通过 ExecutorService.submit 可以注册一个任务到线程池中.
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    public class threadpool {
        public static void main(String[] args) {
            ExecutorService pool= Executors.newFixedThreadPool(10);
            for(int i=0;i<100;i++){
                pool.submit(new Runnable() {
                    @Override
                    public void run() {
                        System.out.println("hello pool");
                    }
                });
            }
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    执行结果:
    在这里插入图片描述

    虽然我们创建的线程池中只有10个线程,而我们线程池中submit了100个任务 因为线程池中的线程执行完一个任务后,可以接着执行下一个任务

    Executors 创建线程池的几种方式

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

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

    工厂方法及工厂模式

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

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

    构造方法受到的限制,比如构造方法的名字必须和类名相同,如果想要实现不同版本的构造,就需要重载,而重载又要受到限制(参数类型,个数,顺序不同)
    在这里插入图片描述
    通过工厂模式解决上述问题:

    //表示一个点
    class Point{
        double x;
        double y;
        double r;
        double a;
    
    
        public void setX(double x) {
            this.x = x;
        }
    
        public void setY(double y) {
            this.y = y;
        }
    
        public void setR(double r) {
            this.r = r;
        }
        public void setA(double a) {
            this.a = a;
        }
    
        public static Point makePointByXY(double x, double y){
           Point point=new Point();
          point.setX(x);
          point.setY(y);
          return point; 
        }
        public static  Point makePointByRA(double r,double a){
            Point point=new Point();
            point.setR(r);
            point.setA(a);
            return point;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    实现线程池

    • 简单实现成可指定线程个数的线程池
    • 核心操作为 submit, 将任务加入线程池中
    • 使用 Runnable 描述一个任务
    • 使用一个 BlockingQueue 组织所有的任务
    • 每个 worker 线程要做的事情: 不停的从 BlockingQueue 中取任务并执行.
    import java.util.concurrent.BlockingQueue;
    import java.util.concurrent.LinkedBlockingQueue;
    
    public 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<n;i++){
        Thread worker=new Thread(()->{
            while(!Thread.currentThread().isInterrupted()){
                try {
                    Runnable runnable = queue.take();
                    runnable.run();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    break;
                }
            }
        });
      worker.start();
     }
     }
    
        public static void main(String[] args) throws InterruptedException {
            MyThreadPool pool=new MyThreadPool(10);
            for (int i = 0; i < 30; i++) {
                pool.submit(new Runnable() {
                    @Override
                    public void run() {
                        System.out.println("hello MyPool");
                    }
                });
            }
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    执行结果:
    在这里插入图片描述
    【补充】:

    线程池和字符串常量池都是广义的概念
    Java里面JVM中实现了字符串常量池,我们在自己写的Java业务代码中,也能实现自己版本的字符串常量池
    线程池在Java标准库中提供里线程池的实现,我们自己也可以写自己版本的实现

  • 相关阅读:
    pytorch深度学习实战lesson20
    武汉智能网联道路智能化建设规范
    systemctl 命令设置开机自启动失败
    Linux 标准IO
    第十四届蓝桥杯模拟赛第一期试题与题解Java
    Python+requests+unittest+excel实现接口自动化测试框架
    作为一个十年卷王,告诫你们年轻人应该如何才能认清自己的价值
    面向对象的三大特性之多态
    方法研究在长安福特总装系统改进中的应用
    61-Java-分布式开发框架Dubbo
  • 原文地址:https://blog.csdn.net/m0_60631323/article/details/126494672