当前位置:首页 > 经验 >

自己动手实现线程池(如何实现一个完整的线程池)

来源:原点资讯(www.yd166.com)时间:2022-11-03 05:50:25作者:YD166手机阅读>>

前言

线程池大家在开发中应该有都有用过,其实线程池就是把一堆线程创建好了放在一个容器中,需要用的时候就直接拿出来用,用完之后再放回池子里。

那线程池有什么好处呢?

自己动手实现线程池,如何实现一个完整的线程池(1)

什么是线程池?

线程池,thread pool,是一种线程使用模式。

为什么要使用线程池?

1:降低资源的消耗,降低线程的创建和销毁的资源消耗。

2:提高响应速度,假设线程的创建时间为T1,执行时间为T2,销毁时间为T3,如果是自己创建线程必然会经历,这三个时间,那么如果创建 销毁>执行,就会有大量时间用在创建和销毁上,而不是在执行任务上,而线程池关注的就是调整T1和T3的时间,线程池可以免去T1和T3的时间。

3:提高线程的可管理性。

不如先来实现一个自己的线程池思想:

1:为了优化T1和T3的时间,在使用前,线程必须在池中已经创建好了,并且可以保持住,既然要保存住,那么就需要一个容器。

2:里面的线程如果是只能跑已经在内部写好的代码,那么就没有意义,所以它必须能接收外部的任务,运行这个任务。

3:外部传入的任务数量大于线程的执行个数是,多余的任务如何处理?emmm,用个容器存起来,用阻塞队列吧,上一章刚写了。

实现代码:

package com.xiangxue.ch6.mypool; import java.util.LinkedList; import java.util.List; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; /** * 类说明:自己线程池的实现 */ public class MyThreadPool2 { // 线程池中默认线程的个数为5 private static int WORK_NUM = 5; // 队列默认任务个数为100 private static int TASK_COUNT = 100; // 工作线程组 private WorkThread[] workThreads; // 任务队列,作为一个缓冲 private final BlockingQueue<Runnable> taskQueue; private final int worker_num;//用户在构造这个池,希望的启动的线程数 // 创建具有默认线程个数的线程池 public MyThreadPool2() { this(WORK_NUM, TASK_COUNT); } // 创建线程池,worker_num为线程池中工作线程的个数 public MyThreadPool2(int worker_num, int taskCount) { if (worker_num <= 0) worker_num = WORK_NUM; if (taskCount <= 0) taskCount = TASK_COUNT; this.worker_num = worker_num; taskQueue = new ArrayBlockingQueue<>(taskCount); workThreads = new WorkThread[worker_num]; for (int i = 0; i < worker_num; i ) { workThreads[i] = new WorkThread(); workThreads[i].start(); } Runtime.getRuntime().availableProcessors(); } // 执行任务,其实只是把任务加入任务队列,什么时候执行有线程池管理器决定 public void execute(Runnable task) { try { taskQueue.put(task); } catch (InterruptedException e) { e.printStackTrace(); } } // 销毁线程池,该方法保证在所有任务都完成的情况下才销毁所有线程,否则等待任务完成才销毁 public void destroy() { // 工作线程停止工作,且置为null System.out.println("ready close pool....."); for (int i = 0; i < worker_num; i ) { workThreads[i].stopWorker(); workThreads[i] = null;//help gc } taskQueue.clear();// 清空任务队列 } // 覆盖toString方法,返回线程池信息:工作线程个数和已完成任务个数 @Override public String toString() { return "WorkThread number:" worker_num " wait task number:" taskQueue.size(); } /** * 内部类,工作线程 */ private class WorkThread extends Thread { @Override public void run() { Runnable r = null; try { while (!isInterrupted()) { r = taskQueue.take(); if (r != null) { System.out.println(getId() " ready exec :" r); r.run(); } r = null;//help gc; } } catch (Exception e) { // TODO: handle exception } } public void stopWorker() { interrupt(); } } }JDK中的线程池和工作机制线程池的创建

ThreadPoolExecutor,jdk所有线程池实现的父类

各个参数的意义

int corePoolSize:线程池核心线程数,池内线程数 < corePoolSize,就会创建新线程, = corePoolSize 就会一直保存这个数量的线程,等以后有任务就会执行,多余的任务会保存在BlockingQueue中

 int maximumPoolSize:线程池允许的最大线程数,如果阻塞队列也满了, < maximumPoolSize的时候就会再次创建新的线程,但是不会 > maximumPoolSize

long keepAliveTime:线程空闲下来的存活时间,这个数值,只有在线程池内的线程数量 > corePoolSize的时候才会有作用,它决定着 > corePoolSize数量的线程的空闲下来的存活时间TimeUnit unit:存活时间单位

BlockingQueue<Runnable> workQueue:保存任务的阻塞队列

ThreadFactory threadFactory:创建线程的工厂,给新创建的线程赋予名字

RejectedExecutionHandler handler: 线程池的饱和策略,当线程池的最大允许数被沾满了,阻塞队列也沾满了,那么再放入任务如何处理

饱和策略:

AbortPolicy:直接抛出异常

CallerRunsPolicy:用调用者所在的线程执行任务

DiscardOldestPolicy:丢弃阻塞队列中最老的任务,也就是最靠前的任务

DiscardPolicy:当前任务直接丢弃

如果这个策略都不是你想要的,可以自己实现 RejectedExecutionHandler 接口,来完成自己的策略,比如写数据库,写日志,或者缓存到其他的里面,等以后再捡回来

注意:刚初始化的线程池当中是没有线程的,只有当往里面投递任务才会创建,如果想一行来就让里面的线程数等于corePoolSize可以调用prestartAllCoreThreads方法

提交任务

void execute(Runnable command):用于提交无返回的任务

Future<T> submit(Callable<T> task):用于提交带返回值的任务

关闭线程池

shutdown(),shutdownNow()

shutdownNow(),设置线程池的状态,还会尝试停止正在运行或者暂停任务的线程

shutdown()设置线程池状态,只会中断所有没有执行任务的线程

工作机制

自己动手实现线程池,如何实现一个完整的线程池(2)

自己动手实现线程池,如何实现一个完整的线程池(3)

在加入任务的会后会先判断一下当前线程池中的核心线程数时候小于corePoolSize,如果小于,创建新的线程如果大于尝试放入阻塞队列中,如果场入失败,那么将尝试创建新的线程用于运行任务,如果创建新的线程也失败的话,那么将执行饱和策略。

合理配置线程池

根据任务的性质来:计算密集型(CPU),IO密集型,混合型

计算密集型:

加密,大数分解,正则.....,线程数适当小一些,

最大推荐:机器的Cpu核心数 1,为什么要 1,防止页缺失

JAVA获取CPU核心数:

Runtime.getRuntime().availableProcessors();IO密集型:

读取文件,数据库连接,网络通讯,线程数适当大一些

最大推荐:机器的CPU核心数*2

混合型:

尽量拆分,IO密集型远远大于计算密集型,拆分意义不大,IO密级型~计算密级型,拆分才有意义

注意:队列的选择上应该使用有界队列,无界队列可能会导致内存溢出,OOM

预定义的线程池:FixedThreadPool:

创建固定线程数量的,适用于负载较重的服务器,使用了LinkedBlockingQueue作为阻塞队列

自己动手实现线程池,如何实现一个完整的线程池(4)

首页 123下一页

栏目热文

线程池的工作原理图解(线程池架构原理图)

线程池的工作原理图解(线程池架构原理图)

为什么需要线程池我们有两种常见的创建线程的方法,一种是继承Thread类,一种是实现Runnable的接口,Thread...

2022-11-03 05:53:12查看全文 >>

中尉转业能安排什么职位(2022军官转业费一览表)

中尉转业能安排什么职位(2022军官转业费一览表)

经历了2021跟2022两次退役之后,很多人清醒地认识到,想要趁着年轻走,难了。想要在30岁之前就转业回家,基本不可能了...

2022-11-03 05:29:05查看全文 >>

上士相当什么级别(上士退伍安排工作吗)

上士相当什么级别(上士退伍安排工作吗)

2月28日关于现役士兵衔级制度的决定通过了新的士兵军衔制度2022年3月31日施行后将会给部队,特别是士兵群体带来怎样的...

2022-11-03 05:20:02查看全文 >>

副连职中尉相当于什么(副连职对应中尉还是少尉)

副连职中尉相当于什么(副连职对应中尉还是少尉)

“连”是属于军队的基层组织,在部队中有着极其重要的位置。新兵下连队,也就是说把新兵分配各个基层部队,而军官下基层挂职也就...

2022-11-03 05:18:55查看全文 >>

中尉可以管多少个人(中尉一般干啥)

中尉可以管多少个人(中尉一般干啥)

<span class="bjh-h3">我国历史中陆军排长到军长分别是什么军衔,管多少人?权利有多大一般玩...

2022-11-03 06:03:54查看全文 >>

2种线程池底层实现原理(线程池底层结构图解)

2种线程池底层实现原理(线程池底层结构图解)

点击上方关注,每天学习一个Java知识点原创: 林湾村龙猫程序的运行,其本质上,是对系统资源(CPU、内存、磁盘、网络等...

2022-11-03 05:40:00查看全文 >>

线程池的基本原理看完就懂了(线程池原理非常详细)

线程池的基本原理看完就懂了(线程池原理非常详细)

随着cpu核数越来越多,不可避免的利用多线程技术以充分利用其计算能力。所以,多线程技术是服务端开发人员必须掌握的技术。线...

2022-11-03 05:58:05查看全文 >>

线程池的工作原理及图解(线程池源码深度解析)

线程池的工作原理及图解(线程池源码深度解析)

前言本文以程序员做需求的例子,比喻线程池的工作过程。以故事白话的方式展开,跟大家阐述线程池工作原理,以方便大家更好理解线...

2022-11-03 05:41:56查看全文 >>

高并发三种解决方法(大数据高并发解决方案)

高并发三种解决方法(大数据高并发解决方案)

一、什么是高并发高并发(High Concurrency)是互联网分布式系统架构设计中必须考虑的因素之一,它通常是指,通...

2022-11-03 06:02:03查看全文 >>

线程池的七个参数(线程池各个参数含义)

线程池的七个参数(线程池各个参数含义)

所谓的线程池的 7 大参数是指,在使用 ThreadPoolExecutor 创建线程池时所设置的 7 个参数,如以下源...

2022-11-03 05:30:06查看全文 >>

文档排行