手写简单线程池

由于业务接触到了多线程,处理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,没有考虑超过丢弃等等问题,只是作为入门

相关推荐
泰山小张只吃荷园4 分钟前
通过SpringTask模拟打印机定时向数据库传入模拟数据
java·开发语言·后端·spring·mybatis
小技与小术18 分钟前
go环境搭建
开发语言·后端·golang
伍肆伍柒20 分钟前
SpringBoot 2.2.10 无法执行Test单元测试
spring boot·后端·单元测试
hummhumm21 分钟前
第 14 章 -Go语言 错误处理
java·开发语言·前端·后端·python·sql·golang
清酒伴风(面试准备中......)1 小时前
计算机网络基础——针对实习面试
计算机网络·面试·职场和发展
Yanbin_Q1 小时前
从 Rust 官方文档理解 Ownership
开发语言·后端·rust
jooLs薯薯熹1 小时前
项目测试 - 哪些工具可以实现测试 Mock?
java·后端
徐寿春1 小时前
SpringBoot -- 自动化装配源码
spring boot·后端·自动化
摆烂工程师2 小时前
GPT4变笨了?教你解决GPT4降智问题!同时封装了个Chrome扩展程序进行检查GPT
前端·后端·程序员
yanessa_yu2 小时前
Maven项目报错:Blocked mirror for repositories
后端·maven