返回顶部
首页 > 资讯 > 精选 >如何实现java简单的线程池
  • 213
分享到

如何实现java简单的线程池

2023-06-20 16:06:04 213人浏览 安东尼
摘要

这篇文章主要讲解了“如何实现java简单的线程池”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“如何实现java简单的线程池”吧!目录拆分实现流程实现方式拒绝策略阻塞队列线程池和工作线程策略模

这篇文章主要讲解了“如何实现java简单的线程池”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“如何实现java简单的线程池”吧!

目录
  • 拆分实现流程

  • 实现方式

    • 拒绝策略

    • 阻塞队列

    • 线程池和工作线程

    • 策略模式

  • 对比jdk的线程池

    • 线程池的状态转化

拆分实现流程

请看下面这张图

如何实现java简单的线程池

首先我们得对线程池进行一个功能拆分

  • Thread Pool 就是我们的线程池,t1,t2,t3代表三个线程

  • Blocking Queue代表阻塞队列

  • main代表main方法的线程

  • task1,task2,task3代表要执行的每个任务

现在我们梳理一下执行的流程,注意这里是简略版的,文章后面我会给出详细版的

如何实现java简单的线程池

所以此时,我们发现了需要创建几个类,或者说几个角色,分别是

  • 线程池

  • 工作线程

  • 阻塞队列

  • 拒绝策略(干嘛的?就是当线程数已经满了,并且阻塞队列也满了,还有任务想进入阻塞队列的时候,就可以拒绝这个任务)

实现方式

1.拒绝策略

@FunctionalInterfaceinterface RejectPolicy<T>{//queue就是我们自己实现的阻塞队列,task是任务    void reject(BlockingQueue<T> queue,T task);}

2.阻塞队列

我们需要实现四个方法,获取和添加,超时获取和超时添加,至于方法实现的细节,我都备注了大量的注释进行解释。

class BlockingQueue<T>{    //阻塞队列    private Deque<T> queue = new ArrayDeque<>();    //    private ReentrantLock lock = new ReentrantLock();    //生产者条件变量    private Condition fullWaitSet = lock.newCondition();    //消费者条件变量    private Condition emptyWaitSet = lock.newCondition();    //容量    private int capacity;    public BlockingQueue(int capacity){        this.capacity = capacity;    }    //带有超时阻塞获取    public T poll(long timeout, TimeUnit timeUnit){        lock.lock();        try {            //将timeout统一转换为纳秒            long nanos = timeUnit.toNanos(timeout);            while(queue.isEmpty()){                try {                    if(nanos <= 0){                        //小于0,说明上次没有获取到,代表已经超时了                        return null;                    }                    //返回值是剩余的时间                    nanos = emptyWaitSet.awaitNanos(nanos);                } catch (InterruptedException e) {                    e.printStackTrace();                }            }            T t = queue.removeFirst();            //通知生产者            fullWaitSet.signal();            return t;        }finally {            lock.unlock();        }    }    //阻塞获取    public T take(){        lock.lock();        try{            while(queue.isEmpty()){ //如果任务队列为空,代表线程池没有可以执行的内容                try {                                         emptyWaitSet.await();                } catch (InterruptedException e) {                    e.printStackTrace();                }            }                        T t = queue.removeFirst();                        fullWaitSet.signal();            //返回任务            return t;        }finally {            lock.unlock();        }    }    //阻塞添加    public void put(T task){        lock.lock();        try {            while(queue.size() == capacity){    //任务队列满了                try {                    System.out.println("等待加入任务队列"+task);                                        fullWaitSet.await();                } catch (InterruptedException e) {                    e.printStackTrace();                }            }            //任务队列还未满            System.out.println("加入任务队列"+task);            //把任务加入阻塞队列            queue.addLast(task);                        emptyWaitSet.signal();        }finally {            lock.unlock();        }    }    //带超时阻塞时间添加    public boolean offer(T task,long timeout,TimeUnit timeUnit){        lock.lock();        try {            long nanos = timeUnit.toNanos(timeout);            while(queue.size() == capacity){                try {                    if(nanos < 0){                        return false;                    }                    System.out.println("等待加入任务队列"+task);                    //不会一直阻塞,超时就会继续向下执行                    nanos = fullWaitSet.awaitNanos(nanos);                } catch (InterruptedException e) {                    e.printStackTrace();                }            }            System.out.println("加入任务队列"+task);            queue.addLast(task);            emptyWaitSet.signal();            return true;        }finally {            lock.unlock();        }    }    //获取任务数量    public int size(){        lock.lock();        try{            return queue.size();        }finally {            lock.unlock();        }    }    //尝试添加任务,如果阻塞队列已经满了,就使用拒绝策略    public void tryPut(RejectPolicy<T> rejectPolicy, T task){        lock.lock();        try {            //判断队列是否已满            if(queue.size() == capacity){                rejectPolicy.reject(this,task);            }else{  //有空闲                System.out.println("加入任务队列"+task);                queue.addLast(task);                emptyWaitSet.signal();            }        }finally {            lock.unlock();        }    }}

3.线程池和工作线程

我把工作线程当成线程池的内部类去实现。方便调用变量。

class ThreadPool{    //阻塞队列    private BlockingQueue<Runnable> taskQueue;    //线程集合    private HashSet<Worker> workers = new HashSet<>();    //核心线程数    private int coreSize;    //获取任务的超时时间    private long timeout;    private TimeUnit timeUnit;    private RejectPolicy<Runnable> rejectPolicy;    public ThreadPool(int coreSize, long timeout, TimeUnit timeUnit, int queueCapacity,RejectPolicy<Runnable> rejectPolicy) {        this.coreSize = coreSize;        this.timeout = timeout;        this.timeUnit = timeUnit;        this.taskQueue = new BlockingQueue<>(queueCapacity);        this.rejectPolicy = rejectPolicy;    }    //执行任务    public void execute(Runnable task){        synchronized (workers){            if(workers.size() <= coreSize){  //当前的线程数小于核心线程数                Worker worker = new Worker(task);                workers.add(worker);                //让线程开始工作,执行它的run方法                worker.start();            }else{                // 1) 死等                // 2) 带超时等待                // 3) 让调用者放弃任务执行                // 4) 让调用者抛出异常                // 5) 让调用者自己执行任务                taskQueue.tryPut(rejectPolicy,task);            }        }    }        class Worker extends Thread{        private Runnable task;        public Worker(Runnable task){            this.task = task;        }        @Override        public void run() {            //执行任务            // 1) 当 task 不为空,执行任务            // 2) 当 task 执行完毕,再接着从任务队列获取任务并执行            while (task != null || (task = taskQueue.poll(timeout, timeUnit)) != null) {                try {                    System.out.println("正在执行的任务" + task);                    task.run();                } catch (Exception e) {                    e.printStackTrace();                } finally {                    //代表这个任务已经执行完了                    task = null;                }            }            synchronized (workers) {                System.out.println("worker 被移除" + this);                workers.remove(this);            }        }    }}

策略模式

细心的小伙伴已经发现,我在拒绝策略这里使用了23种设计模式的策略模式,因为我没有将拒绝的方式写死,而是交给了调用者去实现。

对比JDK的线程池

下面是JDK自带的线程池

如何实现java简单的线程池

经典的七大核心参数

  • corePoolSize:核心线程数

  • queueCapacity:任务队列容量(阻塞队列)

  • maxPoolSize:最大线程数

  • keepAliveTime:线程空闲时间

  • TimeUnit unit:超时时间单位

  • ThreadFactory threadFactory:线程工程

  • rejectedExecutionHandler:任务拒绝处理器

实际上我们自己实现的也大同小异,只不过JDK官方的更为复杂。

JDK线程执行的流程图

如何实现java简单的线程池

如何实现java简单的线程池

线程池的状态转化

线程我们知道在操作系统层面有5种状态

如何实现java简单的线程池

  • 初始状态:仅是在语言层面创建了线程对象,还未与操作系统线程关联

  • 可运行状态(就绪状态):指该线程已经被创建(与操作系统线程关联),可以由 CPU 调度执行

  • 运行状态:指获取了 CPU 时间片运行中的状态,当 CPU 时间片用完,会从【运行状态】转换至【可运行状态】,会导致线程的上下文切换

  • 阻塞状态

  • 如果调用了阻塞 api,如 BIO 读写文件,这时该线程实际不会用到 CPU,会导致线程上下文切换,进入【阻塞状态】

  • 等 BIO 操作完毕,会由操作系统唤醒阻塞的线程,转换至【可运行状态】

  • 与【可运行状态】的区别是,对【阻塞状态】的线程来说只要它们一直不唤醒,调度器就一直不会考虑调度它们

  • 终止状态:表示线程已经执行完毕,生命周期已经结束,不会再转换为其它状态

线程在Java API层面有6种状态

如何实现java简单的线程池

  • NEW 线程刚被创建,但是还没有调用 start() 方法

  • RUNNABLE 当调用了 start() 方法之后,注意,Java API 层面的

  • RUNNABLE 状态涵盖了 操作系统 层面的【可运行状态】、【运行状态】

  • BLOCKED , WAITING , TIMED_WAITING 都是 Java API 层面对【阻塞状态】的细分

  • TERMINATED 当线程代码运行结束

线程池有5种状态

  • RUNNING:能接受新任务,并处理阻塞队列中的任务

  • SHUTDOWN:不接受新任务,但是可以处理阻塞队列中的任务

  • STOP:不接受新任务,并且不处理阻塞队列中的任务,并且还打断正在运行任务的线程,就是直接不干了!

  • TIDYING:所有任务都终止,并且工作线程也为0,处于关闭之前的状态

  • TERMINATED:已关闭。

如何实现java简单的线程池

感谢各位的阅读,以上就是“如何实现java简单的线程池”的内容了,经过本文的学习后,相信大家对如何实现java简单的线程池这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是编程网,小编将为大家推送更多相关知识点的文章,欢迎关注!

--结束END--

本文标题: 如何实现java简单的线程池

本文链接: https://lsjlt.com/news/298110.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • 如何实现java简单的线程池
    这篇文章主要讲解了“如何实现java简单的线程池”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“如何实现java简单的线程池”吧!目录拆分实现流程实现方式拒绝策略阻塞队列线程池和工作线程策略模...
    99+
    2023-06-20
  • Java简单实现线程池
    本文实例为大家分享了Java简单实现线程池的具体代码,供大家参考,具体内容如下 一、线程池 线程池是一种缓冲提高效率的技术。 相当于一个池子,里面存放大量已经创建好的线程,当有一个任...
    99+
    2024-04-02
  • 实现java简单的线程池
    目录拆分实现流程实现方式1.拒绝策略2.阻塞队列3.线程池和工作线程策略模式对比JDK的线程池线程池的状态转化总结拆分实现流程 请看下面这张图 首先我们得对线程池进行一个功能拆分 ...
    99+
    2024-04-02
  • java实现手写一个简单版的线程池
    有些人可能对线程池比较陌生,并且更不熟悉线程池的工作原理。所以他们在使用线程的时候,多数情况下都是new Thread来实现多线程。但是,往往良好的多线程设计大多都是使用线程池来实现...
    99+
    2024-04-02
  • springboot线程池监控的简单实现
    目录背景代码代码类结构线程池扩展类线程工具类线程bean类线程池实现类线程池监控接口类运行结果背景 在我们实际项目开发中,常常会为不同的优先级的任务设置相对应的线程池。一般我们只关注...
    99+
    2024-04-02
  • Linux C线程池简单实现实例
    Linux C线程池 三个文件 1 tpool.h typedef struct tpool_work { void (*routine)(void *); void ...
    99+
    2022-06-04
    线程 实例 简单
  • 用Python实现一个简单的线程池
    线程池的概念是什么?在面向对象编程中,创建和销毁对象是很费时间的,因为创建一个对象要获取内存资源或者其它更多资源。在Java中更是 如此,虚拟机将试图跟踪每一个对象,以便能够在对象销毁后进行垃圾回收。所以提高服务程序效率的一个手段就是尽可能...
    99+
    2023-01-31
    线程 简单 Python
  • 简单聊一聊Java线程池ThreadPoolExecutor
    目录简介参数说明如何创建线程池拒绝策略总结简介 ThreadPoolExecutor是一个实现ExecutorService接口的线程池,ExecutorService是主要用来处理...
    99+
    2024-04-02
  • C++怎么实现一个简单的线程池
    本文小编为大家详细介绍“C++怎么实现一个简单的线程池”,内容详细,步骤清晰,细节处理妥当,希望这篇“C++怎么实现一个简单的线程池”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。一、设计线程池应该包括保存线程的容...
    99+
    2023-06-30
  • Java线程池的简单使用方法实例教程
    目录线程池使用场景? Java线程池使用总结线程池使用场景? java中经常需要用到多线程来处理一些业务,我们非常不建议单纯使用继承Thread或者实现Runnable接口...
    99+
    2024-04-02
  • python进程池的简单实现
    目录创建进程池向进程池提交任务并行执行多个任务关闭进程池等待任务执行完毕Python进程池是Python标准库中multiprocessing模块提供的一种用于管理进程的方式。它可以...
    99+
    2023-03-13
    python进程池
  • 模拟简单Java线程池的方法详解
    目录一、 前言二、线程池是什么?三、线程池构造方法ThreadPoolExecutor的构造方法的参数都是啥意思?四、模拟实现一个线程池总结一、 前言 为了实现并发编程,于是就引入了...
    99+
    2024-04-02
  • C++实现一个简单的线程池的示例代码
    目录一、设计二、参数选择三、类设计一、设计 线程池应该包括 保存线程的容器,保存任务的容器。为了能保证避免线程对任务的竞态获取,需要对任务队列进行加锁。为了使得工作线程感知任务的到来...
    99+
    2024-04-02
  • Java如何使用线程池实现socket编程
    这篇文章主要讲解了“Java如何使用线程池实现socket编程”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Java如何使用线程池实现socket编程”吧!前言以多个客户端和一个服务端的so...
    99+
    2023-06-29
  • Java中线程池自定义如何实现
    这篇“Java中线程池自定义如何实现”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“Java中线程池自定义如何实现”文章吧。线...
    99+
    2023-07-05
  • Java线程池的实现方法
    本篇内容主要讲解“Java线程池的实现方法”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Java线程池的实现方法”吧!以前做的东西,实现一个简单的多线程机制,开始之前,现说说原理性的东西吧,下面...
    99+
    2023-06-17
  • Go简单实现协程池的实现示例
    目录MPG模型通道的特性首先就是进程、线程、协程讲解老三样。 进程: 本质上是一个独立执行的程序,进程是操作系统进行资源分配和调度的基本概念,操作系统进行资源分配和调度的一...
    99+
    2024-04-02
  • GO workPool的线程池如何实现
    今天小编给大家分享一下GO workPool的线程池如何实现的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。Go语言...
    99+
    2023-07-05
  • Linux如何实现C线程池
    这篇文章主要介绍了Linux如何实现C线程池,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。多线程编程,创建一个线程,指定去完成某一个任务,等待线程的退出。虽然能够满足编程需求...
    99+
    2023-06-28
  • C语言如何实现简单的内存池
    本篇内容主要讲解“C语言如何实现简单的内存池”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“C语言如何实现简单的内存池”吧!前言在编程过程中,尤其是对于C语言开发者,其实编程就是在使用内存,不停地...
    99+
    2023-06-20
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作