10分钟带你徒手做个Java线程池

发布时间:2023-04-19 16:04:01

  开发极简版Java线程池需要10分钟,让朋友们更好地理解线程池的核心原理。

  本文从华为云社区分享“放大招式”,冰河带您手持Java线程池10分钟,yyds,赶快收藏吧,作者:冰 河。Java线程池的核心原理

  看过Java线程池源代码的朋友都知道,Java线程池的核心类别是ThreadPolexecutor,而ThreadPolexecutor类别的核心结构方法是具有7个参数的结构方法,如下所示。 public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)

  各参数的含义如下所示。 corePoolSize:常驻核心线程数在线程池中。 maximumPoolSize:线程池可容纳同时执行的最大线程数,大于或等于1。 keepAliveTime:多余的空闲线程存活时间,当空间时间达到keepalivetime值时,多余的线程会被销毁,直到只剩下corepolsize一个线程。 unit:keepalivetime单位。 workQueue:提交但尚未执行的任务队列。 threadFactory:线程工厂表示生成线程池中的工作线程,用户可以创建新的线程,通常可以默认使用。 handler:拒绝策略意味着当线程队列满,工作线程大于等于线程池的最大显示数时(maxnumPoolSize)如何拒绝要求执行的runnable策略?

  而Java的线程池是通过生产者-消费者模式实现的,线程池的用户是生产者,而线程池本身就是消费者。

  Java线程池的核心工作流程如下图所示。

10分钟带你徒手做个Java线程池_线程池

手拉Java线程池

  我们手动实现的线程池比Java自己的线程池简单得多。我们删除了各种复杂的处理方法,只保留了核心原理:线程池用户将任务添加到任务队列中,而线程池本身从任务队列中消耗任务并执行任务。

10分钟带你徒手做个Java线程池_类_02

  只要我们理解了这个核心原理,下一个代码就会简单得多。在实现这个简单的线程池时,我们可以拆卸整个实现过程。拆卸后的实现过程是:定义核心字段,创建内部WorkThread、创建ThreadPool类的结构方法和执行任务的方法。

10分钟带你徒手做个Java线程池_ThreadPoolExecutor_03

定义核心字段

  首先,我们创建了一个名为ThreadPol的Java类别,并在此类别中定义了以下核心字段。 DEFAULT_WORKQUEUE_SIZE:静态常量表示默认阻塞队列的大小。 workQueue:模拟实际的线程池使用阻塞队列来实现生产者-消费者模式。 workThreads:List集合保存线程池内的工作线程用于模拟实际线程池。

  核心代码如下所示。 ///默认阻塞队列大小的privatete static final int DEFAULT_WORKQUEUE_SIZE = 5;///模拟实际线程池使用阻塞队列来实现生产者-消费者模式privatete BlockingQueue workQueue;///模拟实际线程池使用List集合保存线程池内的工作线程private List workThreads = new ArrayList(); 创建内部WordThread

  在ThreadPol类中创建内部WorkThread,模拟线程池中的工作线程。主要功能是消耗workQue中的任务并执行任务。由于工作线程需要从workQue中不断获取任务,因此在这里使用while(true)循环不断尝试消费队列中的任务。

  核心代码如下所示。 ///内部WorkThread,模拟线程池中的工作线程///主要作用是消耗workQueue中的任务,执行///因为工作线程需要不断从workQueue中获取任务,使用while(true)循环不断尝试消费队列中的任务class WorkThread extends Thread{ @Override public void run() { ////不断循环获取队列中的任务 while (true){ //当没有任务时,会阻塞 try { Runnable workTask = workQueue.take(); workTask.run(); } catch (InterruptedException e) { e.printStackTrace(); } } }} 创建ThreadPol类的构造方法

  在这里,我们为ThreadPool创建了两种结构方法,一种是导入线程池的容量大小,另一种是导入线程池的容量大小。

  核心代码如下所示。 ////将线程池的大小和阻塞队列publicc传入ThreadPol的结构方法 ThreadPool(int poolSize, BlockingQueue workQueue){ this.workQueue = workQueue; ///创建polsize工作线程,并将其添加到workthreads集合中 IntStream.range(0, poolSize).forEach((i) -> { WorkThread workThread = new WorkThread(); workThread.start(); workThreads.add(workThread); });}////在ThreadPol的结构方法中输入线程池的大小public ThreadPool(int poolSize){ this(poolSize, new LinkedBlockingQueue<>(DEFAULT_WORKQUEUE_SIZE));} 创建执行任务的方法

  execute()在ThreadPol类中创建执行任务的方法,execute()方法的实现相对简单,即将方法接收到的Runnable任务添加到workQueue队列中。

  核心代码如下所示。 //通过线程池执行任务public void execute(Runnable task){ try { workQueue.put(task); } catch (InterruptedException e) { e.printStackTrace(); }} 完整源码

  在这里,我们给出手动实现的ThreadPol线程池的完整源代码,如下所示。 package io.binghe.thread.pool;import java.util.ArrayList;import java.util.List;import java.util.concurrent.BlockingQueue;import java.util.concurrent.LinkedBlockingQueue;import java.util.stream.IntStream;/** * @author binghe * @version 1.0.0 * @description 自定义线程池 */public class ThreadPool { ///默认情况下阻塞队列的大小 private static final int DEFAULT_WORKQUEUE_SIZE = 5; ///模拟实际线程池使用阻塞队列来实现生产者-消费者模式 private BlockingQueue workQueue; ///模拟实际线程池使用List集合保存线程池内的工作线程 private List workThreads = new ArrayList(); ////将线程池的大小和阻塞队列引入ThreadPol的结构方法 public ThreadPool(int poolSize, BlockingQueue workQueue){ this.workQueue = workQueue; ///创建polsize工作线程,并将其添加到workthreads集合中 IntStream.range(0, poolSize).forEach((i) -> { WorkThread workThread = new WorkThread(); workThread.start(); workThreads.add(workThread); }); } ////将线程池的大小传入ThreadPol的结构方法 public ThreadPool(int poolSize){ this(poolSize, new LinkedBlockingQueue<>(DEFAULT_WORKQUEUE_SIZE)); } ///通过线程池执行任务 public void execute(Runnable task){ try { workQueue.put(task); } catch (InterruptedException e) { e.printStackTrace(); } } ///内部WorkThread,模拟线程池中的工作线程 ///主要作用是消耗workQueue中的任务,执行任务 ///由于工作线程需要从workQueue中不断获取任务,使用while(true)循环不断尝试消费队列中的任务 class WorkThread extends Thread{ @Override public void run() { ////不断循环获取队列中的任务 while (true){ //当没有任务时,会阻塞 try { Runnable workTask = workQueue.take(); workTask.run(); } catch (InterruptedException e) { e.printStackTrace(); } } } }}

  是的,我们只用了几十行Java代码就实现了极简版的Java线程池。是的,这个极简版的Java线程池代码反映了Java线程池的核心原理。

  接下来,我们将测试这个极简版的Java线程池。编制测试程序

  测试程序也比较简单,即通过在main()方法中调用ThreadPol类的结构方法,将其传输到线程池的大小,创建ThreadPol类的实例,然后循环10次调用ThreadPol类的execute()方法,向线程池提交的任务是打印当前线程的名称--->> Hello ThreadPool。

  整体测试代码如下所示。 package io.binghe.thread.pool.test;import io.binghe.thread.pool.ThreadPool;import java.util.stream.IntStream;/** * @author binghe * @version 1.0.0 * @description 测试自定义线程池 */public class ThreadPoolTest { public static void main(String[] args){ ThreadPool threadPool = new ThreadPool(10); IntStream.range(0, 10).forEach((i) -> { threadPool.execute(() -> { System.out.println(Thread.currentThread().getName() + "--->> Hello ThreadPool"); }); }); }}

  接下来,运行ThreadPoltestmain()方法,输出以下信息。 Thread-0--->> Hello ThreadPoolThread-9--->> Hello ThreadPoolThread-5--->> Hello ThreadPoolThread-8--->> Hello ThreadPoolThread-4--->> Hello ThreadPoolThread-1--->> Hello ThreadPoolThread-2--->> Hello ThreadPoolThread-5--->> Hello ThreadPoolThread-9--->> Hello ThreadPoolThread-0--->> Hello ThreadPool

  到目前为止,我们已经开发了自定义的Java线程池。总结

  事实上,线程池的核心原理并不复杂。只要我们耐心分析,深入了解线程池的核心本质,你就会发现线程池的设计是如此优雅。我希望通过这个手写线程池的小例子,你能更好地理解线程池的核心原理。

  点击关注,第一次了解华为云新技术~

上一篇 浅谈Tomcat服务器优化方法
下一篇 @RestController注解开发问题排查

文章素材均来源于网络,如有侵权,请联系管理员删除。

标签: Java教程Java基础Java编程技巧面试题Java面试题