• 线程创建之线程池


    多线程系列文章

    Thread类及常见方法

    线程的状态

    线程安全问题

    多线程案例



    线程池

    什么是线程池

    所谓线程池,其实可以理解为是一个存放线程的池子,我们首先创建线程到线程池中,在需要用到该线程时,再从线程池中取出相应的线程进行加载,使用完毕以后,再归还线程到线程池中;但实际上,线程池更加倾向于是一种解决问题的思想,而并非在内存中有一块线程池的空间;


    为什么要引入线程池

    还记得在进程部分,我们谈到明明进程就可以实现并发编程,为何又要引入线程呢?
    我们说,进程本身是相当重量的,创建进程需要申请资源,销毁进程需要释放资源,在频繁的进程创建销毁的过程中,成本是极高的,因此我们引入了线程;
    线程通过对同一组资源的共用,在一定程度对进程的成本问题做出了优化,但是仅仅这样的优化是不够的,线程进行频繁的创建和销毁同样带来了问题;因此我们引入了线程池的概念,创建线程到线程池,使用完毕以后归还线程到线程池,不再通过系统来创建和销毁,由于从线程池中的操作属于纯用户态操作,而通过系统创建的操作属于内核态操作,因此可以有效提高效率。


    java中的线程池

    java中提供了一个静态工厂来创建不同的线程池,即Executors工厂类;

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

    Executors类中提供了多种创建线程池的方式,上面代码演示的是创建固定线程数的线程池,还有线程数目动态增长的线程池的创建(newCachedThreadPool:)、只包含单个线程的线程池的创建(newSingleThreadExecutor)等,可以在不同的应用场景中自发选择,这里不做过多描述;


    模拟实现一个线程池

    import java.util.concurrent.BlockingQueue;
    import java.util.concurrent.LinkedBlockingQueue;
    
    
    //创建一个线程池类
    class MyThreadPool{
        //以阻塞队列来存放要完成的任务
        private BlockingQueue<Runnable> queue=new LinkedBlockingQueue<>();
        //往线程池中插入任务
        public void submit(Runnable runnable){
            try {
                //把线程放入队列
                queue.put(runnable);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //设置线程池中的线程个数 n
        public MyThreadPool(int n){
            //创建线程来完成执行任务的工作
            for (int i=0;i<n;i++){
                Thread t=new Thread(()->{
                    while (!Thread.currentThread().isInterrupted()){
                        try {
                            //从队列取出线程来执行
                            Runnable runnable=queue.take();
                            runnable.run();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                });
                t.start();
            }
        }
    }
    public class Demo2 {
        public static void main(String[] args) {
            MyThreadPool myThreadPool=new MyThreadPool(10);
            for (int i=0;i<100;i++){
                myThreadPool.submit(new Runnable() {
                    @Override
                    public void run() {
                        System.out.println("MyThreadPool");
                    }
                });
            }
        }
    }
    
    • 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

    这里模拟实现的是一个固定了线程数的线程池,这种线程池的特点是:

    如果线程数没有达到线程池的固定数量,每提交一个任务,线程池内就创建一个新线程,直到线程达到线程池的固定数量;当线程池的大小达到了固定数量时,就会保持不变,直到有线程因为执行异常而结束,线程池中就会补充一个新的线程;
    一般这样一种线程池适用于处理CPU密集型的任务;


    使用线程池,可以有效降低Java线程的创建成本,线程主要解决了以下2个问题:

    线程池带来的好处

    • 提升性能:线程池可以独立负责线程的创建、维护及分配,将任务交给线程池去调度,线程池会尽可能使用空闲的线程去执行任务,对已经创建的线程进行复用,明显提升性能;
    • 线程管理:每个Java线程池都会对线程的一些基本信息进行统计,像完成的任务数量或空闲时间等,可以对线程进行有效的管理,从而实现对任务的高效调度;

    在许多企业的编程规范中,线程必须通过线程池提供,为了减少系统资源的开销,由此也见线程池的优越。


    over!

  • 相关阅读:
    springboot升级过程中踩坑定位分析记录 | 京东云技术团队
    xshell部署前端代码
    解决kotlin写Android项目编译报Execution failed for task ‘:app:kaptDebugKotlin‘.异常
    腾讯云GPU云服务器对比自建GPU服务器有哪些优势?
    2022年深圳技能大赛—大数据技术应用职业技能竞赛圆满闭幕
    ignore()函数不带参数,则它会忽略输入流中的一个字符
    uni-app —— 小程序加入购物车实现过程
    在Ubuntu20.04环境下安装GRPC
    【Android】-- Activity页面(启动和结束、生命周期、启动模式和实例)
    上周热点回顾(2.27-3.5)
  • 原文地址:https://blog.csdn.net/weixin_54175406/article/details/126870675