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

线程池知识梳理

作者: 捉影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条核心线程对象是会一直存在,直到系统退出。

相关文章

  • 线程池知识梳理

    一、为什么要有线程池? 启动线程去做任务可以发挥多核CPU的优势,提高程序执行性能。但频繁的创建、销毁线程对象又会...

  • 多线程知识梳理(6) - 线程池四部曲之 ThreadPoolE

    一、ThreadPoolExecutor 简介 1.1 优点 在 多线程知识梳理(5) - 线程池四部曲之 Exe...

  • Java再回顾(1)—线程池回顾与思考

    概述 本文是针对java线程池的回顾与思考,主要是围绕java线程池的思路梳理与知识总结。长文预警(写这篇真的累到...

  • 线程池创建和相关知识

    线程池创建(单例):Android线程池得要这么用 - 简书 线程池相关知识:Android开发之线程池使用总结 ...

  • 线程

    Java 并发编程:线程池的使用 Java 并发编程:线程池的使用java 多线程核心技术梳理 (附源码) 本文对...

  • Picasso 图片加载库源码分析4-线程池

    PicassoExecutorService 本质上是线程池,所以借此将 Java 线程池这块的关系也梳理一下。 ...

  • 线程池

    主要用于概念扫盲和梳理。本文主要为什么使用线程池,ThreadPoolExecutor构造函数,常见的线程池种类。...

  • java线程池源码解析

    主要介绍线程池相关知识,关于线程池,首先我们思考下为什么要用线程池。如果单纯的使用线程,线程的创建和销毁都是自己来...

  • 线程池(ThreadPoolExecutor)

    一:Executor知识点 二:线程池模型 1:线程池模型:生产者-消费者模式(与一般的池化资源模式不同),线程池...

  • java线程状态和线程池

    本节总结线程相关知识:线程状态和线程池。1.线程的五个状态 关于如何终止线程,以下仅供参考: 2.线程池

网友评论

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

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