手写简单线程池

由于业务接触到了多线程,处理GPS数据,包括通过GPS去计算作业时长这些业务,看了大佬留下的代码晦涩难懂,于是先从简单的用起来,由于业务复杂,大佬采用多线程和EventBus等等 ,本文只先说多线程

前提知识

Deque

Deque (发音为"deck") 是双端队列的缩写,它是一种允许在两端(头部和尾部)进行插入和删除操作的线性数据结构。

  • 双端访问: 这是 deque 最重要的特征。你可以从 deque 的两端高效地添加或移除元素。
  • 高效的插入和删除: 在 deque 的两端进行插入和删除操作通常具有 O(1) 的时间复杂度,这使得它非常适合需要频繁在两端进行操作的应用场景。
  • 动态大小: 大多数 deque 实现都能够动态调整大小,以适应不断变化的数据量。
  • 非线性访问: 虽然可以在两端快速访问,但访问 deque 中间位置的元素则需要线性时间,效率较低。

ReentrantLock

lock

Lock 的接口定义

java 复制代码
public interface Lock {

  
    void lock();

    void lockInterruptibly() throws InterruptedException;

    
    boolean tryLock();

    
    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;

 
    void unlock();

   
    Condition newCondition();
}

通过这些接口函数的定义,我们可以发现 Lock具备一些灵活性,尤其是在它的以下几个特性方面:

  • 支持获取锁超时机制;

  • 支持非阻塞方式获取锁;

  • 支持可中断方式获取锁。

在JDK包中,Lock 实现类的代表莫过于是 ReentrantLock

Condition

Condition 是一种高级别的线程协调工具,它能够让线程在特定的条件下等待或通知其他线程。Condition 可以与 Lock 一起使用,来实现更复杂的线程协调。

以下是 Condition 的常用方法:

  • await():当前线程进入等待状态,直到其他线程调用 signal()signalAll() 方法才会被唤醒。
  • signal():唤醒一个正在等待的线程。
  • signalAll():唤醒所有正在等待的线程。
ReentrantLock

ReentrantLock是一种基于AQS(Abstract Queued Synchronizer)框架的应用实现,是JDK中一种线程并发访问的同步手段,它的功能类似于synchronized是一种互斥锁,可以保证线程安全。

思路

采用经典的设计模型------生产者消费者模型

首先得有得有一个任务等待队列,把任务都丢进去

然后生产者放入任务

消费者消费任务

由于我是自己写一个简易的,就有main负责put任务到任务等待队列

由线程池去task任务

任务等待队列BlockingQueue

java 复制代码
@Slf4j(topic = "c.BlockingQueue")
public class BlockingQueue<T> {
    // 容量
    private int capcity;
    // 双端任务队列容器
    private Deque<T> deque = new ArrayDeque<>();
    // 重入锁
    private ReentrantLock lock = new ReentrantLock();
    // 生产者条件变量
    private Condition fullWaitSet = lock.newCondition();
    // 生产者条件变量
    private Condition emptyWaitSet = lock.newCondition();

    public BlockingQueue(int capcity) {
        this.capcity = capcity;
    }

    // 阻塞的方式添加任务
    public void put(T task) {
        lock.lock();
        try {
            // 通过while的方式
            while (deque.size() >= capcity) {
                log.debug("wait to add queue");
                try {
                    fullWaitSet.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            deque.offer(task);
            log.debug("task add successfully");
            emptyWaitSet.signal();
        }  finally {
            lock.unlock();
        }
    }

    // 阻塞获取任务
    public T take() {
        lock.lock();
        try {
            // 通过while的方式
            while (deque.isEmpty()) {
                try {
                    log.debug("wait to take task");
                    emptyWaitSet.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            fullWaitSet.signal();
            T task = deque.poll();
            log.debug("take task successfully");
            // 从队列中获取元素
            return task;
        } finally {
            lock.unlock();
        }
    }
}

线程池消费端实现

  1. 定义执行器接口
php 复制代码
php 代码解读复制代码/**
 * <p>定义一个执行器的接口:</p>
 *
 * @author: cxw (332059317@qq.com)
 * @date: 2022/10/18  12:31
 * @version: 1.0.0
 */
public interface Executor {

    /**
     * 提交任务执行
     * @param task 任务
     */
    void execute(Runnable task);
}
  1. 定义线程池类实现该接口
java 复制代码
java 代码解读复制代码@Slf4j(topic = "c.ThreadPool")
public class ThreadPool implements Executor {

    /**
     * 任务队列
     */
    private BlockingQueue<Runnable> taskQueue;

    /**
     * 核心工作线程数
     */
    private int coreSize;

    /**
     * 工作线程集合
     */
    private Set<Worker> workers = new HashSet<>();

    /**
     *  创建线程池
     * @param coreSize 工作线程数量
     * @param capcity 阻塞队列容量
     */
    public ThreadPool(int coreSize, int capcity) {
        this.coreSize = coreSize;
        this.taskQueue = new BlockingQueue<>(capcity);
    }

    /**
     * 提交任务执行
     */
    @Override
    public void execute(Runnable task) {
        synchronized (workers) {
            // 如果工作线程数小于阈值,直接开始任务执行
            if(workers.size() < coreSize) {
                Worker worker = new Worker(task);
                workers.add(worker);
                worker.start();
            } else {
                // 如果超过了阈值,加入到队列中
                taskQueue.put(task);
            }
        }
    }

    /**
     * 工作线程,对执行的任务做了一层包装处理
     */
    class Worker extends Thread {
        private Runnable task;

        public Worker(Runnable task) {
            this.task = task;
        }

        @Override
        public void run() {
            // 如果任务不为空,或者可以从队列中获取任务
            while (task != null || (task = taskQueue.take()) != null) {
                try {
                    task.run();
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    // 执行完后,设置任务为空
                    task = null;
                }
            }

              // 移除工作线程
            synchronized (workers){
                log.debug("remove worker successfully");
                workers.remove(this);
            }
        }
    }
}

演示

java 复制代码
@Slf4j(topic = "c.ThreadPool")
public class Main {
    public static void main(String[] args) throws InterruptedException {
        System.out.println("Hello world!");
        Executor executor = new ThreadPool(2, 4);
        // 提交任务
        for (int i = 0; i < 6; i++) {
            final  int j = i;
            executor.execute(() -> {
                try {
                    Thread.sleep(10);
                    log.info("run task {}", j);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
            Thread.sleep(10);
        }

        Thread.sleep(10000);

    }
}

由于只是简单demo,没有考虑超过丢弃等等问题,只是作为入门

相关推荐
她的男孩5 分钟前
多租户和数据权限怎么共存?扒完拦截器注册链路,我找到 4 个隐蔽的坑
java·后端·架构
用户8356290780517 分钟前
使用 Python 设置 Excel 页眉和页脚
后端·python
步行cgn10 分钟前
IoC 控制反转:从概念到 Spring 的实现
后端
爱勇宝25 分钟前
没有 Fn 键关触控板?我做了一个双击即用的 Windows 小工具
前端·后端·程序员
沙湖遇雨1 小时前
8.ByteBuf 的数据模型、生命周期和内存分配
后端
by组态1 小时前
Ricon组态系统API参考手册
前端·后端·物联网
再吃一根胡萝卜1 小时前
Agent 如何选择与生成 SQL —— Text2SQL 全链路详解
后端
苍何1 小时前
把操作流程直接录成 Skill,豆包太夯了!
后端