【多线程】线程池总结带你详细了解线程池

简介: 【多线程】线程池总结带你详细了解线程池

线程池

线程池是一种线程使用模式。线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。避免了在处理短时间任务时创建与销毁线程的代价。线程池不仅能够保证内核的充分利用,还能防止过分调度。线程池最大的好处是减少了每次创建销毁线程的开销。

标准库中的线程池

Executors 创建线程池的几种方式

① 使用 Executors.newFixedThreadPool(5) 能创建出固定包含 5个线程的线程池

返回值类型为 ExecutorService

通过 ExecutorService.submit 可以注册一个任务到线程池中

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPool01 {
    public static void main(String[] args) {
        ExecutorService pool = Executors.newFixedThreadPool(5);
        for (int i = 0; i < 20; i++) {
            pool.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName());
                }
            });
        }
    }
}

② 使用 Executors.newCachedThreadPool()创建出的线程池对象的特点是线程池数目能够动态适应,随着往线程池里添加任务,这个线程池里的线程数量会根据需要自动被创建,创建之后也不会着急销毁,会在线程池里存在一段时间以备随时使用。

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPool2 {
    public static void main(String[] args) {
        ExecutorService pool = Executors.newCachedThreadPool();
        for (int i = 0; i < 20; i++) {
            pool.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName());
                }
            });
        }
    }
}

③使用 Executors.newSingleThreadExecutor(): 创建只包含单个线程的线程池(一般用的不多)

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPool3 {
    public static void main(String[] args) {
        ExecutorService pool = Executors.newSingleThreadExecutor();
        for (int i = 0; i < 20; i++) {
            pool.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName());
                }
            });
        }
    }
}

④使用 Executors.newScheduledThreadPool():相当于是定时器 设定 延迟时间后执行命令,或者定期执行命令

import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
public class TheadPool4 {
    public static void main(String[] args) {
        ExecutorService pool = Executors.newScheduledThreadPool(5);
        for (int i = 0; i < 20; i++) {
            pool.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName());
                }
            });
        }
    }
}

Executors 本质上是 ThreadPoolExecutor 类的封装,ThreadPoolExecutor 提供了更多的可选参数, 可以进一步细化线程池行为的设定。

ThreadPoolExecutor创建线程池

最后一个构造方法可以涵盖上面其他三个,所以我们重点来看最后一个构造方法。

参数详解:

corePoolSize:核心线程数------>我们如果把线程池理解成一个公司,核心线程数就相当于正式员工数量

maximumPoolSize:最大线程数------>正式员工 + 实习生

keepAliveTime:空余线程等待新任务的最大时间------>允许实习生摸鱼的最大时间(如果说正式员工可以摸鱼并且不会被开除,实习生不允许摸鱼)

unit:keepAliveTime的时间单位

workQueue:阻塞队列,用来存放线程池中的任务,可以根据需要来设置这里的队列是啥比如:如果需要优先级则可以设置为PriorityBlockingQueue 如果不需要优先级线程数目是恒定的则可以使用ArrayBlockingQueue 如果任务变动较大可以使用LinkedBlockingQueue

threadFactory:工厂模式的体现,这里使用threadFactory作为工厂类,由这个类负责创建线程,使用工厂类创建线程主要是为了在创建过程中可以很方便对线程的属性进行设置

handler:线程池的拒绝策略,一个线程池能够容纳的任务数量有上限,当持续向线程池中添加任务,一旦达到上限,不同的拒绝策略有不同的效果

拒绝策略:↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓

问题讨论:使用线程池,设置多少线程数目合适呢???

解释:一个线程执行的代码主要有两类:①CPU密集型:代码主要是进行算数运算/逻辑判断 ②IO密集型:代码主要进行的是IO操作

假设一个线程的所有代码都是CPU密集型代码,这时线程池数量不应该超过N(CPU逻辑核心数),即使超过N也无法提高效率,CPU占满了增加线程反而会增加调度的开销。

假设一个线程的所有代码都是IO密集型,这时不占CPU,此时设置线程数就可以超过N,一个核心可以通过调度来并发执行。

但是纯CPU密集型和纯IO密集型代码一般工作中不会出现,所以线程池的线程数量应该对程序进行测试。

模拟实现线程池

模拟实现线程池我们需要知道线程池里有什么:

1、线程池管理器(ThreadPoolManager):用于创建并管理线程池

2、工作线程(WorkThread): 线程池中线程

3、任务接口(Task):每个任务必须实现的接口,以供工作线程调度任务的执行

4、任务队列:用于存放没有处理的任务。提供一种缓冲机制

核心操作为 submit, 将任务加入线程池中,使用一个 BlockingQueue 组织所有的任务 。

模拟实现一个简单版的线程池👇👇👇

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
//模拟实现线程池
public class MyThreadPool {
    //定义一个任务队列
    private BlockingQueue<Runnable> deque = new ArrayBlockingQueue<>(1000);
    //把任务添加到队列中
    public void submit(Runnable runnable) throws InterruptedException {
        deque.put(runnable);
    }
    public MyThreadPool(int n) {
        //创建出n个线程负责上述队列中的任务
        for (int i = 0; i < n; i++) {
            Thread t = new Thread(() -> {
                //让线程消费任务并执行
                try {
                    Runnable runnable = deque.take();
                    runnable.run();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            });
            t.start();
        }
    }
}
相关文章
|
2月前
|
安全 算法 Java
Java 多线程:线程安全与同步控制的深度解析
本文介绍了 Java 多线程开发的关键技术,涵盖线程的创建与启动、线程安全问题及其解决方案,包括 synchronized 关键字、原子类和线程间通信机制。通过示例代码讲解了多线程编程中的常见问题与优化方法,帮助开发者提升程序性能与稳定性。
124 0
|
2月前
|
数据采集 监控 调度
干货分享“用 多线程 爬取数据”:单线程 + 协程的效率反超 3 倍,这才是 Python 异步的正确打开方式
在 Python 爬虫中,多线程因 GIL 和切换开销效率低下,而协程通过用户态调度实现高并发,大幅提升爬取效率。本文详解协程原理、实战对比多线程性能,并提供最佳实践,助你掌握异步爬虫核心技术。
|
3月前
|
Java 数据挖掘 调度
Java 多线程创建零基础入门新手指南:从零开始全面学习多线程创建方法
本文从零基础角度出发,深入浅出地讲解Java多线程的创建方式。内容涵盖继承`Thread`类、实现`Runnable`接口、使用`Callable`和`Future`接口以及线程池的创建与管理等核心知识点。通过代码示例与应用场景分析,帮助读者理解每种方式的特点及适用场景,理论结合实践,轻松掌握Java多线程编程 essentials。
224 5
|
7月前
|
存储 监控 Java
【Java并发】【线程池】带你从0-1入门线程池
欢迎来到我的技术博客!我是一名热爱编程的开发者,梦想是编写高端CRUD应用。2025年我正在沉淀中,博客更新速度加快,期待与你一起成长。 线程池是一种复用线程资源的机制,通过预先创建一定数量的线程并管理其生命周期,避免频繁创建/销毁线程带来的性能开销。它解决了线程创建成本高、资源耗尽风险、响应速度慢和任务执行缺乏管理等问题。
347 60
【Java并发】【线程池】带你从0-1入门线程池
|
5月前
|
Java
线程池是什么?线程池在实际工作中的应用
总的来说,线程池是一种有效的多线程处理方式,它可以提高系统的性能和稳定性。在实际工作中,我们需要根据任务的特性和系统的硬件能力来合理设置线程池的大小,以达到最佳的效果。
137 18
|
7月前
|
Python
python3多线程中使用线程睡眠
本文详细介绍了Python3多线程编程中使用线程睡眠的基本方法和应用场景。通过 `time.sleep()`函数,可以使线程暂停执行一段指定的时间,从而控制线程的执行节奏。通过实际示例演示了如何在多线程中使用线程睡眠来实现计数器和下载器功能。希望本文能帮助您更好地理解和应用Python多线程编程,提高程序的并发能力和执行效率。
210 20
|
7月前
|
安全 Java C#
Unity多线程使用(线程池)
在C#中使用线程池需引用`System.Threading`。创建单个线程时,务必在Unity程序停止前关闭线程(如使用`Thread.Abort()`),否则可能导致崩溃。示例代码展示了如何创建和管理线程,确保在线程中执行任务并在主线程中处理结果。完整代码包括线程池队列、主线程检查及线程安全的操作队列管理,确保多线程操作的稳定性和安全性。
|
4月前
|
机器学习/深度学习 消息中间件 存储
【高薪程序员必看】万字长文拆解Java并发编程!(9-2):并发工具-线程池
🌟 ​大家好,我是摘星!​ 🌟今天为大家带来的是并发编程中的强力并发工具-线程池,废话不多说让我们直接开始。
183 0
|
7月前
|
Linux
Linux编程: 在业务线程中注册和处理Linux信号
通过本文,您可以了解如何在业务线程中注册和处理Linux信号。正确处理信号可以提高程序的健壮性和稳定性。希望这些内容能帮助您更好地理解和应用Linux信号处理机制。
128 26
|
7月前
|
Linux
Linux编程: 在业务线程中注册和处理Linux信号
本文详细介绍了如何在Linux中通过在业务线程中注册和处理信号。我们讨论了信号的基本概念,并通过完整的代码示例展示了在业务线程中注册和处理信号的方法。通过正确地使用信号处理机制,可以提高程序的健壮性和响应能力。希望本文能帮助您更好地理解和应用Linux信号处理,提高开发效率和代码质量。
130 17