• 多线程应用——线程池


    线程池

    1.什么是线程池

    字面意思,一次创建多个线程,放在一个池子(集合类),用的时候拿一个,用完了之后就放回这个池子就可以了。

    2.为什么要用线程池

    1. 首先使用多线程编程就是为了提高效率,势必会创建很多线程,创建的过程是JVM通过调用系统API来申请系统的过程,虽然说创建线程的开销要比创建进程的开销要小的多,但是也架不住特别频繁的创建和销毁,而池化技术就可以减少线程的频繁创建与销毁,从而提高程序性能
    2. JVM调用系统API就意味着从用户态到内核态去执行,而一个系统只有一个内核态,这个内核需要处理很多的事情,所有的进程都是要兼顾到的

    因此使用线程池的最主要的目的是为了提高效率,尽量减少从用户态到内核态的切换

    3.怎么使用线程池

    JDK中提供了一组不同的线程池的实例

    public class Demo01 {
        public static void main(String[] args) {
            // 1. 用来处理大量短时间工作任务的线程池,如果池中没有可用的线程将创建新的线程,如果线程空闲60秒将收回并移出缓存
            ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
            // 2. 创建一个操作无界队列且固定大小线程池
            ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
            // 3. 创建一个操作无界队列且只有一个工作线程的线程池
            ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
            // 4. 创建一个单线程执行器,可以在给定时间后执行或定期执行。
            ScheduledExecutorService singleThreadScheduledExecutor = Executors.newSingleThreadScheduledExecutor();
            // 5. 创建一个指定大小的线程池,可以在给定时间后执行或定期执行。
            ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(3);
            // 6. 创建一个指定大小(不传入参数,为当前机器CPU核心数)的线程池,并行地处理任务,不保证处理顺序
            Executors.newWorkStealingPool();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    以上方法都是用来获取线程池对象的,通过不同的工厂方法获取不同功能的线程池。

    4.工厂模式

    工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

    为什么要使用工厂模式

    这里我们用一个简单的例子来说明原因

    public class Factory {
        public static void main(String[] args) {
            Student student = Student.createByAgeAndName(20, "张三");
            System.out.println(student);
        }
    }
    class Student{
        private int id;
        private int age;
        private String name;
    
    
    
        public int getId() {
            return id;
        }
    
        public void setId(int id) {
            this.id = id;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public Student() {
        }
    
        public Student(int id, String name) { 
            this.id = id;
            this.name = name;
        }
        public Student(int age, String name) {
            this.age = age;
            this.name = name;
        }
    
    
    }
    
    • 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
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51

    观察上述代码,观察一下有什么问题,当我们想通过id或者age来创建一个学生类时,利用构造方法来创建时,出现了'Student(int, String)' is already defined in... 这里的语法不符合Java语法中重载的语法规则,因此我们使用工厂模式可以解决这类问题。

    public class Factory {
        
    }
    class Student{
        private int id;
        private int age;
        private String name;
    
    
    
        public int getId() {
            return id;
        }
    
        public void setId(int id) {
            this.id = id;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public Student() {
        }
    
        // 通过方法名的区分来分别实现不同的创建对象的方法
        public static Student createByIdAndName(int id,String name){
            Student student=new Student();
            student.setId(id);
            student.setName(name);
            return student;
        }
    
        public static Student createByAgeAndName(int age,String name){
            Student student=new Student();
            student.setAge(age);
            student.setName(name);
            return student;
        }
    
    }
    
    • 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
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53

    对于工厂模式可以参考以下教程 工厂模式

    5.自己实现一个线程池

    实现步骤:

    1. 管理任务的一个队列,可以用阻塞队列去实现,使用阻塞队列的好处是,当线程去取任务时,如果队列为空那么就阻塞等待,不会造成过多的CPU资源消耗
    2. 提供一个往队列中添加任务的方法
    3. 创建多个线程,扫描这个队列,如果有任务就拿出来执行
    public class MyThreadPool{
        //定义一个阻塞队列来管理任务
        BlockingQueue<Runnable>queue=new LinkedBlockingQueue<>();
    
        /**
         * 提供一个往队列中添加任务的方法
         * @param runnable
         * @throws InterruptedException
         */
        public void submit(Runnable runnable) throws InterruptedException {
            queue.put(runnable);
        }
    
        /**
         * 提供一个指定了创建线程数的构造方法
         * @param num
         */
        public MyThreadPool(int num){
            if(num<=0){
                throw new RuntimeException("线程数必须大于0");
            }
            // 创建线程
            for (int i = 0; i < num; i++) {
                Thread thread = new Thread(() -> {
                    while (true){
                        try {
                            Runnable runnable=queue.take();
                            runnable.run();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                });
                //启动线程
                thread.start();
            }
        }
    }
    
    • 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

    6.创建系统自带的线程池

    在开发过程中一般使用ThreadPoolExecutor这个类来创建线程池,以下为每个参数的代表意义

    pool

    代码实现

    public class Demo {
        public static void main(String[] args) {
            ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                    3,//核心线程数
                    10,//最大线程数
                    1,//临时线程的存活时间
                    TimeUnit.SECONDS,//临时线程的存活时间单位
                    new LinkedBlockingQueue<>(20),//阻塞队列的类型和大小
            );
            for (int i = 0; i < 100; i++) {
                int taskId=i;
                threadPoolExecutor.submit(()->{
                    System.out.println("执行任务 " +taskId+",当前线程:"+Thread.currentThread().getName());
                });
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    6.1 拒绝策略

    image-20230906175334118

    6.2 线程池的工作流程

    image-20230906175032534
    关于线程池的分享就到这里了,看完留下的你们的三连吧,你们的支持是我最大的动力!!!

  • 相关阅读:
    ASO优化之手游该如何获得巨量新增(中)
    小剧场短剧影视小程序源码,附带系统搭建教程
    机器学习-数学基础
    计算机保研复习
    C++【个人笔记1】
    思腾云计算
    为什么Proteus串口无法正常显示
    HCIA-R&S(15)OSPF基础、OSPF核心工作流程
    97. 交错字符串 java解决
    不用Swagger,那我用啥?
  • 原文地址:https://blog.csdn.net/weixin_60781793/article/details/132720646