自定义线程池

本文档探讨了自定义线程池的实现,主要关注其核心组件——BlockingQueue。讲解了BlockingQueue的编写思路,选用ArrayDeque作为基础结构,ReentrantLock作为锁,并引入生产者和消费者条件变量。详细阐述了take方法和pull方法的阻塞逻辑,以及size方法的实现。此外,还概述了自定义线程池的后续开发计划,包括任务提交、Worker实现及拒绝策略等关键环节。
摘要由CSDN通过智能技术生成

目录

自定义线程池

BlockingQueue 的编写思路

阻塞队列方法编写

poll增强(待更新)

自定义线程池的实现(待更新)

任务提交 和 Worker的实现(待更新)

take死等和 poll超时(待更新)

当队伍线程已满(待更新)

offer增强(待更新)

拒绝策略(待更新)


本篇学习笔记利用自定义线程池去更深入的理解JDK的线程池API。

线程是一种系统资源,每创建一个新的线程,就要占用一定的内存,当在高并发的情况下,来了很多任务,如果要为每个任务创建一个线程,将耗费很大的系统资源,对内存的占用很大。

线程并不是越多越好,频繁的上下文切换也降低系统性能,尤其在高并发的情况下。

出于以上的原因,有了线程池的出现。

线程池可以让线程重复利用,既可以内存的占用,也可以减少线程的数量,避免频繁发生上下文的切换。

自定义线程池

自定义线程池需要两个组件,ThreadPool线程池),BlockingQueue(阻塞队列),这两个组件。

ThreadPool里会有一些线程,让这些线程能够重复的利用。

Blocking Queue,它体现的是在一种生产者和消费者模式下,平衡它们之间差异的组件。

ThreadPool里的线程 相当于消费者,它去不断的获取任务和执行任务。除此以外还有生产者线程,它会源源不断的产生新的任务,在这个模式下,它们的速率是不一致的。

比如生产者,如果迟迟没有提交新任务,线程池里的线程就需要在阻塞队列里等待去执行任务。

又或者,生产者产生了太多的任务,线程们忙不过来,多出来的任务也需要放进阻塞队列里面等待被执行。

所以阻塞队列再此扮演了很重要的角色,它是生产者和消费者之间的桥梁。

先从阻塞队列入手,来自定义线程池。

BlockingQueue 的编写思路

通过对BlockingQueue的结构理出如下思路,看看编写BlockingQueue需要些什么东西 :

BlockingQueue 是一个任务队列,用于存放任务对象。这个队列的数据结构应该选择一个链表,因为队列的特性是先进先出。

链表结构可以用 Deque 类,它是一个双向链表,通过ArrayDeque来实现链表,因为它的效率高于LinkedList。

还需要,线程池有多个线程,它们都需要从任务队列头部获取任务,多个线程不可能同时获取正任务,只有一个线程能获取成功,其他线程则需要等待拿去下个任务。所以需要利用锁来保护头部元素,同理队列尾部也应该受到锁的保护,因为可能有多个线程都往队列里添加新任务。

锁的实现选择 ReentrantLock,这样比较灵活。

还需要两个变量,一个消费者的条件变量,和一个生产者的条件变量,消费者在没有任务的时候需要等待,而生产者也不能无限制的往队列里添加元素,阻塞队列都有容量限制,在阻塞队列容量已满的时候,它也需要去等待。因此需要消费者和生产者的条件变量。

还需要为阻塞队列定义容量,因为阻塞队列要有容量限制。

阻塞队列还需要一些个方法,一个方法来获取元素,一个方法来添加元素,一个方法来获取阻塞队列的大小,因为需要知道队列里的任务还有多少个。

阻塞队列需要的属性和方法一览:

  • 阻塞队列
  • 生产者和消费者的条件变量
  • 阻塞获取的方法
  • 阻塞添加的方法
  • 得到队列任务多少的方法

结构一览:

 类一览(仅做结构展示,方法还未实现):

/**
 * @author Claw
 * @date 2022/3/8 22:49.
 */
public class BlockingQueue<T> {
    // 任务队列
    private Deque<T> queue = new LinkedList();
    // 锁
    private ReentrantLock lock = new ReentrantLock();
    // 生产者条件变量
    private Condition fullWaitSet = lock.newCondition();
    // 消费者条件变量
    private Condition emptyWaitSet = lock.newCondition();
    // 容量
    private int capacity;

    /**
     * 阻塞获取
     *
     * @return
     */
    public T take() {

    }

    /**
     * 阻塞添加
     *
     * @return
     */
    public void pull() {

    }

    /**
     * 获取任务数量
     *
     * @return
     */
    public int size() {

    }
}

阻塞队列方法编写

take方法要从队列中获取元素,需要判断队列是否为空,如果为空则需要等待, 如果不为空则拿取队列头部元素(队列先入先出特性),再唤醒在等待放入任务的生产者线程。

pull方法要为队列中添加元素,需要判断队列是否已满,如果已满则需要等待,如果队列没有满,则在尾部添加元素,同时唤醒等待拿去任务的消费者线程。

size方法则获取队列里的元素多少即可。

    /**
     * 阻塞获取
     *
     * @return
     */
    public T take() {
        lock.unlock();
        try {
            // 获取之前,判断队列是否为空,如果为空则需要等待
            while (queue.isEmpty()) {
                try {
                    fullWaitSet.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            // 如果不为空 则拿取队列头部元素 因为队列特性 拿取头部元素 也是移除头部元素 因此方法是removeFirst()
            T t = queue.removeFirst();
            // 唤醒等待放入队列的生产者线程
            fullWaitSet.signal();
            return t;
        } finally {
            lock.unlock();
        }
    }

    /**
     * 阻塞添加
     *
     * @return
     */
    public void pull(T element) {
        lock.lock();
        try {
            // 当阻塞队列容量已满,则进入等待
            while (queue.size() == capacity) {
                try {
                    fullWaitSet.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            // 当阻塞队列没有满时,则可以往里添加元素(添加至尾部)
            queue.addLast(element);
            // 唤醒等待的消费者线程
            emptyWaitSet.signal();
        } finally {
            lock.unlock();
        }
    }

    /**
     * 获取任务数量
     *
     * @return
     */
    public int size() {
        lock.lock();
        try {
            return queue.size();
        } finally {
            lock.unlock();
        }
    }

poll增强(待更新)

自定义线程池的实现(待更新)

任务提交 和 Worker的实现(待更新)

take死等和 poll超时(待更新)

当队伍线程已满(待更新)

offer增强(待更新)

拒绝策略(待更新)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
线程池自定义线程池工厂指的是我们可以通过自定义工厂类来创建线程池。在Java中,我们可以通过实现ThreadFactory接口来自定义线程池工厂。通过自定义工厂类,我们可以定制线程的创建方式,例如给线程设置特定的命名规则、设置线程的优先级等。 自定义线程池工厂的步骤如下: 1. 创建一个实现ThreadFactory接口的自定义工厂类,并实现其`newThread(Runnable r)`方法。 2. 在`newThread`方法中,我们可以通过`Thread`类的构造方法来创建线程,并进行一些定制化的操作,比如设置线程的名称、优先级等。 3. 自定义线程池工厂类的实例化后,我们可以将其作为参数传递给线程池创建方法中,以便使用自定义线程池工厂来创建线程池。 举个例子,假设我们需要自定义线程池工厂来创建线程池,可以按照以下步骤进行: 1. 创建一个自定义线程池工厂类,例如`CustomThreadFactory`,并实现ThreadFactory接口。 2. 在`CustomThreadFactory`类中,实现`newThread(Runnable r)`方法,并在该方法中创建线程,并设置线程的名称。 3. 在使用线程池的地方,例如`Executors.newFixedThreadPool()`方法中,将`CustomThreadFactory`类的实例传递给`newFixedThreadPool()`方法,以使用自定义线程池工厂来创建线程池。 通过自定义线程池工厂,我们可以更加灵活地控制线程的创建过程,并根据实际需求进行定制化操作。这样可以提高线程池的灵活性和可扩展性,使其更好地适用于各种场景的需求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值