对多线程中线程池的理解

简介: 对多线程中线程池的理解

一.概念理解

何为线程池?

线程池的释义正如它的命名:专门用来存放线程的池子(集合类),也就是将线程存储于集合类,使用时从线程池中直接获取,使用结束后将线程放回集合类即可,这样就避免了线程频繁的创建和销毁。

为什么要使用线程池?

我们使用多线程的目的在于利用线程间的阻塞空隙,来提高效率。但是我们使多线程时不免创建多个线程,线程的创建虽然相比于进程的创建所用的开销要小很多,但是线程频繁的开启和销毁仍然会降低处理任务的效率,所以线程池应运而生。我们都知道,创建系统层面的线程需要JVM调用系统层面的API,通过系统创建PCB实现线程的创建,这是在系统中的内核态中完成的,而我们JAVA中实现的功能是在JAVA 层面的用户态中实现的,为了提高效率,我们尽量将工作安排在用户态中,而线程池也是基于这种思想下创建的。

好处:

降低资源消耗:减少线程的创建和销毁带来的性能开销。

提高响应速度:当任务来时可以直接使用,不用等待线程创建

可管理性: 进行统一的分配,监控,避免大量的线程间因互相抢占系统资源导致的阻塞现象

二.如何使用线程池?

  //1.用来处理大量短时间工作任务的线程池,如果池中没有可用的线程将创建新的线程,线程空闲60s则会被回收

       ExecutorService cachedThreadPool = Executors.newCachedThreadPool();

       //2.创建一个无界队列且固定大小的线程池

       ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);

       //3.创建一个无界队列且只有一个工作线程的线程池

       ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();

       //4.创建一个单线程执行器,可以在给定时间后执行或者定期执行

       ScheduledExecutorService singleScheduleExecutor = Executors.newSingleThreadScheduledExecutor();

       //5.创建一个指定大小的线程池,可以在给定时间后执行或者是定期执行

       ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(3);

       //6.创建一个指定大小的线程池(不指定大小则为当前机器的cpu核心数),并行执行任务,不保证执行顺序

       ExecutorService workStealingPool = Executors.newWorkStealingPool();

JVM提供了6种默认的线程池,分别对应不同的应用场景,但是事实上我们很少用JVM提供的线程池,而是根据我们自己的业务场景创建自定义的线程池。为什么呢?一是因为系统提供的线程池难以满足我们的业务场景,二是当线程创建临时线程时,直接创建最大线程容量,因此在这种开辟策略下 系统提供的默认线程池开销极大。


2341169712924a1a2a6fcbab78ac51a0.png

而JVM提供的线程池体现了通过提供不同的方法来获取不同的线程池,这便是工厂模式的体现,在这里关于工厂模式我们不做过多介绍,大家参考以下博客:https://blog.csdn.net/weixin_43757333/article/details/126294625


三.我们手动实现一个线程池


我们先说一下线程池的实现思路:①自定义实现MyThreadPool类,其中通过阻塞队列实现对任务的存储②通过submit()方法将任务提交至阻塞队列中③通过类中的构造方法将任务通过不同的线程取出并执行

代码如下:

public class MyThreadPool {

  BlockingQueue<Runnable>runnables=new LinkedBlockingQueue<>();//阻塞队列

  //创建提交任务的方法

  public void submit(Runnable runnable) throws InterruptedException {

      runnables.put(runnable);

  }

  //创建取出任务的方法(构造方法)

   public MyThreadPool(int num){

      //检查num的合法性

       if(num<=0){

           throw new RuntimeException("输入的数字不合法");

       }

       //循环进行不断创建线程

       for(int i=0;i<num;++i){

            Thread thread =new Thread(()->{

                while (true){

                    try {

                        Runnable take = runnables.take();

                        take.run();

                    } catch (InterruptedException e) {

                        throw new RuntimeException(e);

                    }

                }

 

            });

            thread.start();

       }

   }

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

e301cb1c63468ebe3bea6494834dc747.png

我们通过一个实际生活中的例子来对这些概念进行解释:

beb4f5dbb0f781c51b1a8707c0e272c9.png

那么线程池的工作流程是怎样的呢?

6ce291199cb280700c9c7f56a36b6f4f.png

关于四种不同的拒绝策略:

c05e906b1390821cddec6dc21a2998ec.png

创建方式如下:


c3671950d0d7bee56497e34f659970ae.png


相关文章
|
16天前
|
Java 数据库 Android开发
【专栏】Kotlin在Android开发中的多线程优化,包括线程池、协程的使用,任务分解、避免阻塞操作以及资源管理
【4月更文挑战第27天】本文探讨了Kotlin在Android开发中的多线程优化,包括线程池、协程的使用,任务分解、避免阻塞操作以及资源管理。通过案例分析展示了网络请求、图像处理和数据库操作的优化实践。同时,文章指出并发编程的挑战,如性能评估、调试及兼容性问题,并强调了多线程优化对提升应用性能的重要性。开发者应持续学习和探索新的优化策略,以适应移动应用市场的竞争需求。
|
4天前
|
Java 数据库
【Java多线程】对线程池的理解并模拟实现线程池
【Java多线程】对线程池的理解并模拟实现线程池
13 1
|
16天前
|
Java 程序员 数据库
Java线程池让使用线程变得更加高效
使用一个线程需要经过创建、运行、销毁三大步骤,如果业务系统每个线程都要经历这个过程,那会带来过多不必要的资源消耗。线程池就是为了解决这个问题而生,需要时就从池中拿取,使用完毕就放回去,池化思想通过复用对象大大提高了系统的性能。线程池、数据库连接池、对象池等都采用了池化技术,下面我们就来学习下线程池的核心知识、面试重点~
54 5
Java线程池让使用线程变得更加高效
|
4天前
|
设计模式 消息中间件 安全
【Java多线程】关于多线程的一些案例 —— 单例模式中的饿汉模式和懒汉模式以及阻塞队列
【Java多线程】关于多线程的一些案例 —— 单例模式中的饿汉模式和懒汉模式以及阻塞队列
9 0
|
4天前
|
Java
【Java多线程】分析线程加锁导致的死锁问题以及解决方案
【Java多线程】分析线程加锁导致的死锁问题以及解决方案
12 1
|
4天前
|
存储 缓存 安全
【Java多线程】线程安全问题与解决方案
【Java多线程】线程安全问题与解决方案
12 1
|
4天前
|
Java 调度
【Java多线程】线程中几个常见的属性以及状态
【Java多线程】线程中几个常见的属性以及状态
9 0
|
4天前
|
Java 调度
【Java多线程】对进程与线程的理解
【Java多线程】对进程与线程的理解
11 1
|
5天前
|
存储 安全 Java
【探索Linux】P.21(多线程 | 线程同步 | 条件变量 | 线程安全)
【探索Linux】P.21(多线程 | 线程同步 | 条件变量 | 线程安全)
11 0
|
5天前
|
算法 安全 Linux
【探索Linux】P.20(多线程 | 线程互斥 | 互斥锁 | 死锁 | 资源饥饿)
【探索Linux】P.20(多线程 | 线程互斥 | 互斥锁 | 死锁 | 资源饥饿)
11 0