美文网首页
线程池知识梳理

线程池知识梳理

作者: 捉影T_T900 | 来源:发表于2020-07-15 09:13 被阅读0次

    一、为什么要有线程池?

    启动线程去做任务可以发挥多核CPU的优势,提高程序执行性能。但频繁的创建、销毁线程对象又会导致整体系统的执行效率不高,甚至出现严重问题。所以要引入“池化”概念。预先准备一些资源在池子中,有需要时就从池子中获取资源;如果资源已经被用光,再有请求过来则需要等待资源被释放后才能使用空闲的资源。达到重复利用,提高整体性能。

    二、线程池做了什么?

    线程池核心工作过程:

    1、初始化线程池,指定线程池的大小
    2、向线程池中放入任务执行
    3、如果线程池中创建的线程数目未达到指定大小,则创建我们自定义的线程类放入线程池集合,并立刻执行任务。执行完毕后该线程会一直监听队列
    4、如果线程池中创建的线程数目已满,则将任务放入阻塞缓存队列中(阻塞队列保证队列的执行顺序,不会有并发问题)
    5、线程池中所有创建的线程,都会一直从缓存队列中取任务,取到任务立马执行
    

    三、如何自行实现一个线程池?

    public class CustomThreadPool {
    
        // 存放线程的集合
        private ArrayList<CustomThread> threads;
    
        // 任务队列
        private ArrayBlockingQueue<Runnable> taskQueue;
    
        // 线程初始化限定大小
        private int threadNum;
    
        // 已经工作的线程数目
        private int workThreadNum;
    
        private final ReentrantLock mainLock = new ReentrantLock();
    
        public CustomThreadPool(int initPoolNum) {
            threadNum = initPoolNum;
            threads = new ArrayList<>(initPoolNum);
    
            // 任务队列初始化为线程池线程数的四倍
            taskQueue = new ArrayBlockingQueue<Runnable>(initPoolNum * 4);
    
            workThreadNum = 0;
        }
    
        public void execute(Runnable runnable) {
            try {
                mainLock.lock();
    
                // 线程池未满,每加入一个任务开启一个线程
                if (workThreadNum < threadNum) {
                    CustomThread customThread = new CustomThread(runnable);
                    customThread.start();
                    threads.add(customThread);
                    workThreadNum++;
                } else { // 线程池已满,放入任务队列,等待空闲线程执行
                    // 队列已满,无法再添加任务,则会拒绝这个任务
                    // offer 的作用,在不超出队列长度的情况下在队列尾部插入元素,如果成功则返回true,如果失败则返回false
                    if (!taskQueue.offer(runnable)) {
                        rejectTask();
                    }
                }
            } finally {
                mainLock.unlock();
            }
        }
    
        private void rejectTask() {
            System.out.println("任务队列已满,无法再添加,请扩大你的初始线程数量");
        }
    
    
        class CustomThread extends Thread {
    
            private Runnable task;
    
            public CustomThread(Runnable runnable) {
                this.task = runnable;
            }
    
            @Override
            public void run() {
                super.run();
                // 该线程一直启动,不断从任务队列中取出任务执行
                while (true) {
                    // 如果初始化任务不为空,则直接执行初始化任务
                    if (task != null) {
                        task.run();
                        task = null;
                    } else { // 如果没有初始化任务,则从任务队列中获取任务并执行
                        Runnable queueTask = taskQueue.poll();
                        if (queueTask != null) {
                            queueTask.run();
                        }
                    }
                }
            }
        }
    
    }
    
    

    测试过程

           CustomThreadPool pool = new CustomThreadPool(5);
    
            Runnable task = new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName() + "   执行中");
                }
            };
    
            for (int i = 0; i < 20; i++) {
                pool.execute(task);
            }
    

    简单总结:
    在不断添加任务的过程中,会优先启动核心线程,直到核心线程数达到5,这5个核心线程会一直在后面运行,处理完自己携带的任务后,就会继续从阻塞队列中获取缓存任务,由于是阻塞队列,一个线程在获取任务的过程中,其他线程会等待这个线程获取成功后再获取任务。获取完任务后就会执行,然后线程进行下一次的轮询继续获取任务并进行执行。说明这5条核心线程对象是会一直存在,直到系统退出。

    相关文章

      网友评论

          本文标题:线程池知识梳理

          本文链接:https://www.haomeiwen.com/subject/hiyecktx.html